Commit 6495ca55 authored by Lev Walkin's avatar Lev Walkin

Fix XER decoder of INTEGER, Issue #344.

    In some cases an INTEGER overflow during parsing is not detected
    and incorrect value is returned to the decoder instead of an error.
    Reported by Nika Pona <npona@digamma.ai>.
    (Severity: low; Seuciry impact: medium).
parent 88ed3b5c
...@@ -35,6 +35,11 @@ ...@@ -35,6 +35,11 @@
(Severity: low; Security impact: none) (Severity: low; Security impact: none)
* Fix XER decoder crash on maliciously constructed ENUMERATED input. * Fix XER decoder crash on maliciously constructed ENUMERATED input.
(Severity: medium; Security impact: medium) (Severity: medium; Security impact: medium)
* Fix XER decoder of INTEGER, OBJECT IDENTIFIER, and RELATIVE-OID.
In some cases an INTEGER overflow during parsing is not detected
and incorrect value is returned to the decoder instead of an error.
Reported by Nika Pona <npona@digamma.ai>.
(Severity: low; Security impact: medium).
FIXES IN TOOLING: FIXES IN TOOLING:
* CVE-2017-12966 verified not present. * CVE-2017-12966 verified not present.
......
/*- /*
* Copyright (c) 2003-2014 Lev Walkin <vlm@lionet.info>. * Copyright (c) 2003-2019 Lev Walkin <vlm@lionet.info>.
* All rights reserved. * All rights reserved.
* Redistribution and modifications are permitted subject to BSD license. * Redistribution and modifications are permitted subject to BSD license.
*/ */
...@@ -1035,10 +1035,9 @@ asn_strtoimax_lim(const char *str, const char **end, intmax_t *intp) { ...@@ -1035,10 +1035,9 @@ asn_strtoimax_lim(const char *str, const char **end, intmax_t *intp) {
int sign = 1; int sign = 1;
intmax_t value; intmax_t value;
#define ASN1_INTMAX_MAX ((~(uintmax_t)0) >> 1) const intmax_t asn1_intmax_max = ((~(uintmax_t)0) >> 1);
const intmax_t upper_boundary = ASN1_INTMAX_MAX / 10; const intmax_t upper_boundary = asn1_intmax_max / 10;
intmax_t last_digit_max = ASN1_INTMAX_MAX % 10; intmax_t last_digit_max = asn1_intmax_max % 10;
#undef ASN1_INTMAX_MAX
if(str >= *end) return ASN_STRTOX_ERROR_INVAL; if(str >= *end) return ASN_STRTOX_ERROR_INVAL;
...@@ -1056,9 +1055,7 @@ asn_strtoimax_lim(const char *str, const char **end, intmax_t *intp) { ...@@ -1056,9 +1055,7 @@ asn_strtoimax_lim(const char *str, const char **end, intmax_t *intp) {
} }
for(value = 0; str < (*end); str++) { for(value = 0; str < (*end); str++) {
switch(*str) { if(*str >= 0x30 && *str <= 0x39) {
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: {
int d = *str - '0'; int d = *str - '0';
if(value < upper_boundary) { if(value < upper_boundary) {
value = value * 10 + d; value = value * 10 + d;
...@@ -1070,17 +1067,27 @@ asn_strtoimax_lim(const char *str, const char **end, intmax_t *intp) { ...@@ -1070,17 +1067,27 @@ asn_strtoimax_lim(const char *str, const char **end, intmax_t *intp) {
sign = 1; sign = 1;
value = -value * 10 - d; value = -value * 10 - d;
} }
} else { str += 1;
if(str < *end) {
// If digits continue, we're guaranteed out of range.
*end = str; *end = str;
if(*str >= 0x30 && *str <= 0x39) {
return ASN_STRTOX_ERROR_RANGE; return ASN_STRTOX_ERROR_RANGE;
} else {
*intp = sign * value;
return ASN_STRTOX_EXTRA_DATA;
}
} }
break;
} else { } else {
*end = str; *end = str;
return ASN_STRTOX_ERROR_RANGE; return ASN_STRTOX_ERROR_RANGE;
} }
} else {
*end = str;
return ASN_STRTOX_ERROR_RANGE;
} }
continue; } else {
default:
*end = str; *end = str;
*intp = sign * value; *intp = sign * value;
return ASN_STRTOX_EXTRA_DATA; return ASN_STRTOX_EXTRA_DATA;
...@@ -1102,10 +1109,9 @@ enum asn_strtox_result_e ...@@ -1102,10 +1109,9 @@ enum asn_strtox_result_e
asn_strtoumax_lim(const char *str, const char **end, uintmax_t *uintp) { asn_strtoumax_lim(const char *str, const char **end, uintmax_t *uintp) {
uintmax_t value; uintmax_t value;
#define ASN1_UINTMAX_MAX ((~(uintmax_t)0)) const uintmax_t asn1_uintmax_max = ((~(uintmax_t)0));
const uintmax_t upper_boundary = ASN1_UINTMAX_MAX / 10; const uintmax_t upper_boundary = asn1_uintmax_max / 10;
uintmax_t last_digit_max = ASN1_UINTMAX_MAX % 10; uintmax_t last_digit_max = asn1_uintmax_max % 10;
#undef ASN1_UINTMAX_MAX
if(str >= *end) return ASN_STRTOX_ERROR_INVAL; if(str >= *end) return ASN_STRTOX_ERROR_INVAL;
...@@ -1121,26 +1127,34 @@ asn_strtoumax_lim(const char *str, const char **end, uintmax_t *uintp) { ...@@ -1121,26 +1127,34 @@ asn_strtoumax_lim(const char *str, const char **end, uintmax_t *uintp) {
} }
for(value = 0; str < (*end); str++) { for(value = 0; str < (*end); str++) {
switch(*str) { if(*str >= 0x30 && *str <= 0x39) {
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: {
unsigned int d = *str - '0'; unsigned int d = *str - '0';
if(value < upper_boundary) { if(value < upper_boundary) {
value = value * 10 + d; value = value * 10 + d;
} else if(value == upper_boundary) { } else if(value == upper_boundary) {
if(d <= last_digit_max) { if(d <= last_digit_max) {
value = value * 10 + d; value = value * 10 + d;
} else { str += 1;
if(str < *end) {
// If digits continue, we're guaranteed out of range.
*end = str; *end = str;
if(*str >= 0x30 && *str <= 0x39) {
return ASN_STRTOX_ERROR_RANGE; return ASN_STRTOX_ERROR_RANGE;
} else {
*uintp = value;
return ASN_STRTOX_EXTRA_DATA;
}
} }
break;
} else { } else {
*end = str; *end = str;
return ASN_STRTOX_ERROR_RANGE; return ASN_STRTOX_ERROR_RANGE;
} }
} else {
*end = str;
return ASN_STRTOX_ERROR_RANGE;
} }
continue; } else {
default:
*end = str; *end = str;
*uintp = value; *uintp = value;
return ASN_STRTOX_EXTRA_DATA; return ASN_STRTOX_EXTRA_DATA;
......
...@@ -178,6 +178,129 @@ check_xer(int lineno, int tofail, char *xmldata, long orig_value) { ...@@ -178,6 +178,129 @@ check_xer(int lineno, int tofail, char *xmldata, long orig_value) {
ASN_STRUCT_FREE(asn_DEF_INTEGER, st); ASN_STRUCT_FREE(asn_DEF_INTEGER, st);
} }
static void
check_strtoimax() {
const intmax_t intmax_max = ((~(uintmax_t)0) >> 1);
const intmax_t intmax_min = -((intmax_t)intmax_max) - 1;
char positive_max[32];
char negative_min[32];
const int len_pmax = snprintf(positive_max, sizeof(positive_max),
"+%" ASN_PRIdMAX, intmax_max);
const int len_nmin = snprintf(negative_min, sizeof(negative_min),
"%" ASN_PRIdMAX, intmax_min);
assert(len_pmax < (int)sizeof(positive_max));
assert(len_nmin < (int)sizeof(negative_min));
enum asn_strtox_result_e result;
intmax_t value;
/*
* Test edge values first.
*/
// Positive.
const char *last_pmax = &positive_max[len_pmax];
result = asn_strtoimax_lim(positive_max, &last_pmax, &value);
assert(result == ASN_STRTOX_OK);
assert(last_pmax == &positive_max[len_pmax]);
assert(value == intmax_max);
// Negative.
const char *last_nmin = &negative_min[len_nmin];
result = asn_strtoimax_lim(negative_min, &last_nmin, &value);
assert(result == ASN_STRTOX_OK);
assert(last_nmin == &negative_min[len_nmin]);
assert(value == intmax_min);
/*
* Test one smaller than edge evalues.
*/
positive_max[len_pmax - 1]--;
negative_min[len_nmin - 1]--;
// Positive.
result = asn_strtoimax_lim(positive_max, &last_pmax, &value);
assert(result == ASN_STRTOX_OK);
assert(last_pmax == &positive_max[len_pmax]);
assert(value == intmax_max - 1);
// Negative.
result = asn_strtoimax_lim(negative_min, &last_nmin, &value);
assert(result == ASN_STRTOX_OK);
assert(last_nmin == &negative_min[len_nmin]);
assert(value == intmax_min + 1);
/*
* Test one bigger than edge evalues.
*/
positive_max[len_pmax - 1] += 2;
negative_min[len_nmin - 1] += 2;
// Positive.
value = 42;
result = asn_strtoimax_lim(positive_max, &last_pmax, &value);
assert(result == ASN_STRTOX_ERROR_RANGE);
assert(last_pmax == &positive_max[len_pmax - 1]);
assert(value == 42);
// Negative.
value = 42;
result = asn_strtoimax_lim(negative_min, &last_nmin, &value);
assert(result == ASN_STRTOX_ERROR_RANGE);
assert(last_nmin == &negative_min[len_nmin - 1]);
assert(value == 42);
/*
* Get back to the edge.
* Append an extra digit at the end.
*/
positive_max[len_pmax - 1]--;
negative_min[len_nmin - 1]--;
assert(len_pmax < (int)sizeof(positive_max) - 1);
assert(len_nmin < (int)sizeof(negative_min) - 1);
strcat(positive_max, "0");
strcat(negative_min, "0");
last_pmax++;
last_nmin++;
value = 42;
result = asn_strtoimax_lim(positive_max, &last_pmax, &value);
assert(result == ASN_STRTOX_OK);
assert(value == intmax_max);
result = asn_strtoimax_lim(negative_min, &last_nmin, &value);
assert(result == ASN_STRTOX_OK);
assert(value == intmax_min);
}
/*
* Check that asn_strtoimax_lim() always reaches the end of the numeric
* sequence, even if it can't fit into the range.
*/
static void
check_strtoimax_span() {
const intmax_t intmax_max = ((~(uintmax_t)0) >> 1);
const intmax_t almost_min = -((intmax_t)(intmax_max - 10));
char buf[64];
intmax_t value = 42;
enum asn_strtox_result_e result;
// Check a particular way to integer overflow.
// Check that we scan until the very end.
int len = snprintf(buf, sizeof(buf), "%" PRIdMAX "0</end>", almost_min);
assert(len < (int)sizeof(buf));
const char *nmlast = &buf[len];
result = asn_strtoimax_lim(buf, &nmlast, &value);
assert(*nmlast == '0');
assert((ptrdiff_t)(nmlast - buf) == (ptrdiff_t)(len - strlen("0</end>")));
assert(result == ASN_STRTOX_ERROR_RANGE);
assert(value == 42);
// Check a particular way to integer overflow.
// Check that we scan until the very end.
len = snprintf(buf, sizeof(buf), "%" PRIdMAX "</end>", almost_min);
assert(len < (int)sizeof(buf));
nmlast = &buf[len];
result = asn_strtoimax_lim(buf, &nmlast, &value);
assert(*nmlast == '<');
assert((ptrdiff_t)(nmlast - buf) == (ptrdiff_t)(len - strlen("</end>")));
assert(result == ASN_STRTOX_EXTRA_DATA);
assert(value == almost_min);
}
int int
main() { main() {
uint8_t buf1[] = { 1 }; uint8_t buf1[] = { 1 };
...@@ -298,5 +421,8 @@ main() { ...@@ -298,5 +421,8 @@ main() {
} }
#endif #endif
check_strtoimax();
check_strtoimax_span();
return 0; return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment