diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c index 471fbb7ee6..a6e9d00727 100644 --- a/src/backend/utils/adt/numutils.c +++ b/src/backend/utils/adt/numutils.c @@ -104,10 +104,10 @@ static const int8 hexlookup[128] = { * pg_strtoint16() will throw ereport() upon bad input format or overflow; * while pg_strtoint16_safe() instead returns such complaints in *escontext, * if it's an ErrorSaveContext. -* - * NB: Accumulate input as an unsigned number, to deal with two's complement + * + * NB: Accumulate input as a negative number, to deal with two's complement * representation of the most negative number, which can't be represented as a - * signed positive number. + * positive number. */ int16 pg_strtoint16(const char *s) @@ -120,7 +120,7 @@ pg_strtoint16_safe(const char *s, Node *escontext) { const char *ptr = s; const char *firstdigit; - uint16 tmp = 0; + int16 tmp = 0; bool neg = false; /* skip leading spaces */ @@ -145,10 +145,11 @@ pg_strtoint16_safe(const char *s, Node *escontext) { if (isxdigit((unsigned char) *ptr)) { - if (unlikely(tmp > -(PG_INT16_MIN / 16))) - goto out_of_range; + int8 digit = hexlookup[(unsigned char) *ptr++]; - tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++]; + if (unlikely(pg_mul_s16_overflow(tmp, 16, &tmp)) || + unlikely(pg_sub_s16_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -169,10 +170,11 @@ pg_strtoint16_safe(const char *s, Node *escontext) { if (*ptr >= '0' && *ptr <= '7') { - if (unlikely(tmp > -(PG_INT16_MIN / 8))) - goto out_of_range; + int8 digit = (*ptr++ - '0'); - tmp = tmp * 8 + (*ptr++ - '0'); + if (unlikely(pg_mul_s16_overflow(tmp, 8, &tmp)) || + unlikely(pg_sub_s16_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -193,10 +195,11 @@ pg_strtoint16_safe(const char *s, Node *escontext) { if (*ptr >= '0' && *ptr <= '1') { - if (unlikely(tmp > -(PG_INT16_MIN / 2))) - goto out_of_range; + int8 digit = (*ptr++ - '0'); - tmp = tmp * 2 + (*ptr++ - '0'); + if (unlikely(pg_mul_s16_overflow(tmp, 2, &tmp)) || + unlikely(pg_sub_s16_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -217,10 +220,11 @@ pg_strtoint16_safe(const char *s, Node *escontext) { if (isdigit((unsigned char) *ptr)) { - if (unlikely(tmp > -(PG_INT16_MIN / 10))) - goto out_of_range; + int8 digit = (*ptr++ - '0'); - tmp = tmp * 10 + (*ptr++ - '0'); + if (unlikely(pg_mul_s16_overflow(tmp, 10, &tmp)) || + unlikely(pg_sub_s16_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -248,18 +252,15 @@ pg_strtoint16_safe(const char *s, Node *escontext) if (unlikely(*ptr != '\0')) goto invalid_syntax; - if (neg) + if (!neg) { - /* check the negative equivalent will fit without overflowing */ - if (tmp > (uint16) (-(PG_INT16_MIN + 1)) + 1) + /* could fail if input is most negative number */ + if (unlikely(tmp == PG_INT16_MIN)) goto out_of_range; - return -((int16) tmp); + tmp = -tmp; } - if (tmp > PG_INT16_MAX) - goto out_of_range; - - return (int16) tmp; + return tmp; out_of_range: ereturn(escontext, 0, @@ -298,7 +299,7 @@ pg_strtoint32_safe(const char *s, Node *escontext) { const char *ptr = s; const char *firstdigit; - uint32 tmp = 0; + int32 tmp = 0; bool neg = false; /* skip leading spaces */ @@ -323,10 +324,11 @@ pg_strtoint32_safe(const char *s, Node *escontext) { if (isxdigit((unsigned char) *ptr)) { - if (unlikely(tmp > -(PG_INT32_MIN / 16))) - goto out_of_range; + int8 digit = hexlookup[(unsigned char) *ptr++]; - tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++]; + if (unlikely(pg_mul_s32_overflow(tmp, 16, &tmp)) || + unlikely(pg_sub_s32_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -347,10 +349,11 @@ pg_strtoint32_safe(const char *s, Node *escontext) { if (*ptr >= '0' && *ptr <= '7') { - if (unlikely(tmp > -(PG_INT32_MIN / 8))) - goto out_of_range; + int8 digit = (*ptr++ - '0'); - tmp = tmp * 8 + (*ptr++ - '0'); + if (unlikely(pg_mul_s32_overflow(tmp, 8, &tmp)) || + unlikely(pg_sub_s32_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -371,10 +374,11 @@ pg_strtoint32_safe(const char *s, Node *escontext) { if (*ptr >= '0' && *ptr <= '1') { - if (unlikely(tmp > -(PG_INT32_MIN / 2))) - goto out_of_range; + int8 digit = (*ptr++ - '0'); - tmp = tmp * 2 + (*ptr++ - '0'); + if (unlikely(pg_mul_s32_overflow(tmp, 2, &tmp)) || + unlikely(pg_sub_s32_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -395,10 +399,11 @@ pg_strtoint32_safe(const char *s, Node *escontext) { if (isdigit((unsigned char) *ptr)) { - if (unlikely(tmp > -(PG_INT32_MIN / 10))) - goto out_of_range; + int8 digit = (*ptr++ - '0'); - tmp = tmp * 10 + (*ptr++ - '0'); + if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) || + unlikely(pg_sub_s32_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -426,18 +431,15 @@ pg_strtoint32_safe(const char *s, Node *escontext) if (unlikely(*ptr != '\0')) goto invalid_syntax; - if (neg) + if (!neg) { - /* check the negative equivalent will fit without overflowing */ - if (tmp > (uint32) (-(PG_INT32_MIN + 1)) + 1) + /* could fail if input is most negative number */ + if (unlikely(tmp == PG_INT32_MIN)) goto out_of_range; - return -((int32) tmp); + tmp = -tmp; } - if (tmp > PG_INT32_MAX) - goto out_of_range; - - return (int32) tmp; + return tmp; out_of_range: ereturn(escontext, 0, @@ -461,9 +463,9 @@ invalid_syntax: * while pg_strtoint64_safe() instead returns such complaints in *escontext, * if it's an ErrorSaveContext. * - * NB: Accumulate input as an unsigned number, to deal with two's complement + * NB: Accumulate input as a negative number, to deal with two's complement * representation of the most negative number, which can't be represented as a - * signed positive number. + * positive number. */ int64 pg_strtoint64(const char *s) @@ -476,7 +478,7 @@ pg_strtoint64_safe(const char *s, Node *escontext) { const char *ptr = s; const char *firstdigit; - uint64 tmp = 0; + int64 tmp = 0; bool neg = false; /* skip leading spaces */ @@ -501,10 +503,11 @@ pg_strtoint64_safe(const char *s, Node *escontext) { if (isxdigit((unsigned char) *ptr)) { - if (unlikely(tmp > -(PG_INT64_MIN / 16))) - goto out_of_range; + int8 digit = hexlookup[(unsigned char) *ptr++]; - tmp = tmp * 16 + hexlookup[(unsigned char) *ptr++]; + if (unlikely(pg_mul_s64_overflow(tmp, 16, &tmp)) || + unlikely(pg_sub_s64_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -525,10 +528,11 @@ pg_strtoint64_safe(const char *s, Node *escontext) { if (*ptr >= '0' && *ptr <= '7') { - if (unlikely(tmp > -(PG_INT64_MIN / 8))) - goto out_of_range; + int8 digit = (*ptr++ - '0'); - tmp = tmp * 8 + (*ptr++ - '0'); + if (unlikely(pg_mul_s64_overflow(tmp, 8, &tmp)) || + unlikely(pg_sub_s64_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -549,10 +553,11 @@ pg_strtoint64_safe(const char *s, Node *escontext) { if (*ptr >= '0' && *ptr <= '1') { - if (unlikely(tmp > -(PG_INT64_MIN / 2))) - goto out_of_range; + int8 digit = (*ptr++ - '0'); - tmp = tmp * 2 + (*ptr++ - '0'); + if (unlikely(pg_mul_s64_overflow(tmp, 2, &tmp)) || + unlikely(pg_sub_s64_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -573,10 +578,11 @@ pg_strtoint64_safe(const char *s, Node *escontext) { if (isdigit((unsigned char) *ptr)) { - if (unlikely(tmp > -(PG_INT64_MIN / 10))) - goto out_of_range; + int8 digit = (*ptr++ - '0'); - tmp = tmp * 10 + (*ptr++ - '0'); + if (unlikely(pg_mul_s64_overflow(tmp, 10, &tmp)) || + unlikely(pg_sub_s64_overflow(tmp, digit, &tmp))) + goto out_of_range; } else if (*ptr == '_') { @@ -604,18 +610,15 @@ pg_strtoint64_safe(const char *s, Node *escontext) if (unlikely(*ptr != '\0')) goto invalid_syntax; - if (neg) + if (!neg) { - /* check the negative equivalent will fit without overflowing */ - if (tmp > (uint64) (-(PG_INT64_MIN + 1)) + 1) + /* could fail if input is most negative number */ + if (unlikely(tmp == PG_INT64_MIN)) goto out_of_range; - return -((int64) tmp); + tmp = -tmp; } - if (tmp > PG_INT64_MAX) - goto out_of_range; - - return (int64) tmp; + return tmp; out_of_range: ereturn(escontext, 0,