0
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <ctype.h>

static bool
ReadDimensionInt(char **srcptr, int *result, const char *origStr)
{
    char       *p = *srcptr;
    // printf("p=%s\n", p);
    /* don't accept leading whitespace */
    if (!isdigit((unsigned char) *p) && *p != '-' && *p != '+')
    {
        *result = 0;
        return true;            /* leave 'src' unmodified */
    }

    errno = 0;
    *result = strtol(p, srcptr, 10);

    if (errno == ERANGE)
    {
        printf("array bound is out of range\n");
        return false;
    }
    return true;
}

int 
main(void)
{

    char    *s = "2147483648111";
    char    *result;
    int testresult;
    bool    readok;

    readok = ReadDimensionInt(&s, &testresult, s);
    
    if (readok)
        printf("test_result=%d\n", testresult);
    else
        printf("read dimension failed");

    return 0;
}

expect invoke printf("array bound is out of range\n"); https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtol.html

printf("%zu\n", sizeof(long)); return 8.

jian
  • 4,119
  • 1
  • 17
  • 32
  • Try `printf("%zu\n", sizeof(long));`. What does that print? – user3386109 Sep 02 '23 at 02:53
  • 1
    You are doing `*result = strtol(p, srcptr, 10);` when `result` is `int *`. That can easily be invalid if `LONG_MAX != INT_MAX` on your system. Also, if you have 64-bit `long` on your system, `2147483648111` is well within range. – Marco Bonelli Sep 02 '23 at 03:02
  • @user3386109 return 8. – jian Sep 02 '23 at 03:06
  • 2
    Well there you have it. Your `long` is 64-bit. The max supported value should be `9223372036854775807` (a bit higher than `2147483648111` :P). Additionally, your `int` is going to be 32-bit, so your assignment `*result = strtol(p, srcptr, 10)` is invalid. Your `result` should be `long *` not `int *`. Either change it or use a temporary `long` variable and check its bounds before assigning it to an `int`. – Marco Bonelli Sep 02 '23 at 03:07
  • As any other integer type, the size (and range) of `long` depends on the compiler and the target platform. Windows and MSVC have 32-bit `long` even on 64-bit systems. GCC typically uses 64-bit `long` on all 64-bit systems, and 32-bit `long` on 32-bit systems. – Some programmer dude Sep 02 '23 at 03:08
  • seems this is very bullet-proof. https://stackoverflow.com/a/12923949/15603477 will try this one. @MarcoBonelli – jian Sep 02 '23 at 03:13

2 Answers2

1

strtol converts to a long and only gives ERANGE if the result is out of range for long. You appear to be converting to int so you actually want something more like:

errno = 0;
long val = strtol(p, srcptr, 10);
if (errno == ERANGE || val > INT_MAX || val < INT_MIN) {
    printf("array bound is out of range\n");
    return false;
} else {
    *result = val;
}
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
0

enter image description here

Please note that return value strtol save to long type or long long type.

The depend on how many size of sizeof(long) or sizeof(long long), if sizeof(long) is 4 in 32-bit computer, then output array bound is out of range, if sizeof(long) is 8 in 64-bit computer, then output test_result=2147483648111 when you save return of strtol function as long at sizeof(long) is 8 status.

And in your code if (!isdigit((unsigned char) *p) && *p != '-' && *p != '+'), if check it should return false rather then return true. (Note that you set value of *result to 0).

And if (!isdigit((unsigned char) *p) && *p != '-' && *p != '+') should be if (!isdigit((unsigned char) *p) || *p == '-' || *p == '+'), because of when *p is not digit or *p is '-' or *p is '+', then should return false. *p is either not a number or - or +, there is only one status, so this mean if (!isdigit((unsigned char) *p) && *p != '-' && *p != '+') is alway check failed, code in if (!isdigit((unsigned char) *p) && *p != '-' && *p != '+') will never run.

see below code that I change type of result int to long when declare, so parameter is long* result.

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>

static bool
ReadDimensionInt(char **srcptr, long *result, const char *origStr)
{
    char       *p = *srcptr;
    // printf("p=%s\n", p);
    /* don't accept leading whitespace */
    if (!isdigit((unsigned char) *p) || *p == '-' || *p == '+')
    {
        *result = 0;
        printf("******conversion could not be performed\n");
        return false;            /* leave 'src' unmodified */
    }

    errno = 0;
    *result = strtol(p, srcptr, 10);

    if (errno == ERANGE)
    {
        printf("array bound is out of range\n");
        return false;
    }else if (errno == EINVAL){
        printf("conversion could not be performed\n");
        *result = 0;
        return false;
    }
    return true;
}

int 
main(void)
{

    char    *s = "2147483648111";
    char    *result;
    long testresult = 1;
    bool    readok;
    
    printf("sizeof long is %zu, signed long max is %ld\n", sizeof(long), LONG_MAX);

    readok = ReadDimensionInt(&s, &testresult, s);
    
    if (readok)
        printf("test_result=%ld\n", testresult);
    else
        printf("read dimension failed\n");

    return 0;
}

Run it will output:

sizeof long is 8, signed long max is 9223372036854775807
test_result=2147483648111
Tom
  • 417
  • 3
  • 10