8000 Fix possible read past end of string in to_timestamp(). · dpirotte/postgres@7bad282 · GitHub 8000
[go: up one dir, main page]

Skip to content

Commit 7bad282

Browse files
committed
Fix possible read past end of string in to_timestamp().
to_timestamp() handles the TH/th format codes by advancing over two input characters, whatever those are. It failed to notice whether there were two characters available to be skipped, making it possible to advance the pointer past the end of the input string and keep on parsing. A similar risk existed in the handling of "Y,YYY" format: it would advance over three characters after the "," whether or not three characters were available. In principle this might be exploitable to disclose contents of server memory. But the security team concluded that it would be very hard to use that way, because the parsing loop would stop upon hitting any zero byte, and TH/th format codes can't be consecutive --- they have to follow some other format code, which would have to match whatever data is there. So it seems impractical to examine memory very much beyond the end of the input string via this bug; and the input string will always be in local memory not in disk buffers, making it unlikely that anything very interesting is close to it in a predictable way. So this doesn't quite rise to the level of needing a CVE. Thanks to Wolf Roediger for reporting this bug.
1 parent bfc39da commit 7bad282

File tree

1 file changed

+39
-45
lines changed

1 file changed

+39
-45
lines changed

src/backend/utils/adt/formatting.c

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,6 @@ static void dump_node(FormatNode *node, int max);
968968
static char *get_th(char *num, int type);
969969
static char *str_numth(char *dest, char *num, int type);
970970
static int strspace_len(char *str);
971-
static int strdigits_len(char *str);
972971
static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode);
973972
static void from_char_set_int(int *dest, const int value, const FormatNode *node);
974973
static int from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node);
@@ -1969,9 +1968,19 @@ asc_toupper_z(const char *buff)
19691968

19701969
/* ----------
19711970
* Skip TM / th in FROM_CHAR
1971+
*
1972+
* If S_THth is on, skip two chars, assuming there are two available
19721973
* ----------
19731974
*/
1974-
#define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
1975+
#define SKIP_THth(ptr, _suf) \
1976+
do { \
1977+
if (S_THth(_suf)) \
1978+
{ \
1979+
if (*(ptr)) (ptr)++; \
1980+
if (*(ptr)) (ptr)++; \
1981+
} \
1982+
} while (0)
1983+
19751984

19761985
#ifdef DEBUG_TO_FROM_CHAR
19771986
/* -----------
@@ -2054,23 +2063,6 @@ strspace_len(char *str)
20542063
return len;
20552064
}
20562065

2057-
static int
2058-
strdigits_len(char *str)
2059-
{
2060-
char *p = str;
2061-
int len;
2062-
2063-
len = strspace_len(str);
2064-
p += len;
2065-
2066-
while (*p && isdigit((unsigned char) *p) && len <= DCH_MAX_ITEM_SIZ)
2067-
{
2068-
len++;
2069-
p++;
2070-
}
2071-
return len;
2072-
}
2073-
20742066
/*
20752067
* Set the date mode of a from-char conversion.
20762068
*
@@ -2941,19 +2933,19 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
29412933
case DCH_HH12:
29422934
from_char_parse_int_len(&out->hh, &s, 2, n);
29432935
out->clock = CLOCK_12_HOUR;
2944-
s += SKIP_THth(n->suffix);
2936+
SKIP_THth(s, n->suffix);
29452937
break;
29462938
case DCH_HH24:
29472939
from_char_parse_int_len(&out->hh, &s, 2, n);
2948-
s += SKIP_THth(n->suffix);
2940+
SKIP_THth(s, n->suffix);
29492941
break;
29502942
case DCH_MI:
29512943
from_char_parse_int(&out->mi, &s, n);
2952-
s += SKIP_THth(n->suffix);
2944+
SKIP_THth(s, n->suffix);
29532945
break;
29542946
case DCH_SS:
29552947
from_char_parse_int(&out->ss, &s, n);
2956-
s += SKIP_THth(n->suffix);
2948+
SKIP_THth(s, n->suffix);
29572949
break;
29582950
case DCH_MS: /* millisecond */
29592951
len = from_char_parse_int_len(&out->ms, &s, 3, n);
@@ -2964,7 +2956,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
29642956
out->ms *= len == 1 ? 100 :
29652957
len == 2 ? 10 : 1;
29662958

2967-
s += SKIP_THth(n->suffix);
2959+
SKIP_THth(s, n->suffix);
29682960
break;
29692961
case DCH_US: /* microsecond */
29702962
len = from_char_parse_int_len(&out->us, &s, 6, n);
@@ -2975,11 +2967,11 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
29752967
len == 4 ? 100 :
29762968
len == 5 ? 10 : 1;
29772969

2978-
s += SKIP_THth(n->suffix);
2970+
SKIP_THth(s, n->suffix);
29792971
break;
29802972
case DCH_SSSS:
29812973
from_char_parse_int(&out->ssss, &s, n);
2982-
s += SKIP_THth(n->suffix);
2974+
SKIP_THth(s, n->suffix);
29832975
break;
29842976
case DCH_tz:
29852977
case DCH_TZ:
@@ -3018,7 +3010,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30183010
break;
30193011
case DCH_MM:
30203012
from_char_parse_int(&out->mm, &s, n);
3021-
s += SKIP_THth(n->suffix);
3013+
SKIP_THth(s, n->suffix);
30223014
break;
30233015
case DCH_DAY:
30243016
case DCH_Day:
@@ -3036,29 +3028,29 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30363028
break;
30373029
case DCH_DDD:
30383030
from_char_parse_int(&out->ddd, &s, n);
3039-
s += SKIP_THth(n->suffix);
3031+
SKIP_THth(s, n->suffix);
30403032
break;
30413033
case DCH_IDDD:
30423034
from_char_parse_int_len(&out->ddd, &s, 3, n);
3043-
s += SKIP_THth(n->suffix);
3035+
SKIP_THth(s, n->suffix);
30443036
break;
30453037
case DCH_DD:
30463038
from_char_parse_int(&out->dd, &s, n);
3047-
s += SKIP_THth(n->suffix);
3039+
SKIP_THth(s, n->suffix);
30483040
break;
30493041
case DCH_D:
30503042
from_char_parse_int(&out->d, &s, n);
30513043
out->d--;
3052-
s += SKIP_THth(n->suffix);
3044+
SKIP_THth(s, n->suffix);
30533045
break;
30543046
case DCH_ID:
30553047
from_char_parse_int_len(&out->d, &s, 1, n);
3056-
s += SKIP_THth(n->suffix);
3048+
SKIP_THth(s, n->suffix);
30573049
break;
30583050
case DCH_WW:
30593051
case DCH_IW:
30603052
from_char_parse_int(&out->ww, &s, n);
3061-
s += SKIP_THth(n->suffix);
3053+
SKIP_THth(s, n->suffix);
30623054
break;
30633055
case DCH_Q:
30643056

@@ -3073,34 +3065,36 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30733065
* isn't stored anywhere in 'out'.
30743066
*/
30753067
from_char_parse_int((int *) NULL, &s, n);
3076-
s += SKIP_THth(n->suffix);
3068+
SKIP_THth(s, n->suffix);
30773069
break;
30783070
case DCH_CC:
30793071
from_char_parse_int(&out->cc, &s, n);
3080-
s += SKIP_THth(n->suffix);
3072+
SKIP_THth(s, n->suffix);
30813073
break;
30823074
case DCH_Y_YYY:
30833075
{
30843076
int matched,
30853077
years,
3086-
millenia;
3078+
millenia,
3079+
nch;
30873080

3088-
matched = sscanf(s, "%d,%03d", &millenia, &years);
3089-
if (matched != 2)
3081+
matched = sscanf(s, "%d,%03d%n", &millenia, &years, &nch);
3082+
if (matched < 2)
30903083
ereport(ERROR,
30913084
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
30923085
errmsg("invalid input string for \"Y,YYY\"")));
30933086
years += (millenia * 1000);
30943087
from_char_set_int(&out->year, years, n);
30953088
out->yysz = 4;
3096-
s += strdigits_len(s) + 4 + SKIP_THth(n->suffix);
3089+
s += nch;
3090+
SKIP_THth(s, n->suffix);
30973091
}
30983092
break;
30993093
case DCH_YYYY:
31003094
case DCH_IYYY:
31013095
from_char_parse_int(&out->year, &s, n);
31023096
out->yysz = 4;
3103-
s += SKIP_THth(n->suffix);
3097+
SKIP_THth(s, n->suffix);
31043098
break;
31053099
case DCH_YYY:
31063100
case DCH_IYY:
@@ -3115,7 +3109,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31153109
out->year += 1000;
31163110
else
31173111
out->year += 2000;
3118-
s += SKIP_THth(n->suffix);
3112+
SKIP_THth(s, n->suffix);
31193113
break;
31203114
case DCH_YY:
31213115
case DCH_IY:
@@ -3130,7 +3124,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31303124
out->year += 2000;
31313125
else
31323126
out->year += 1900;
3133-
s += SKIP_THth(n->suffix);
3127+
SKIP_THth(s, n->suffix);
31343128
break;
31353129
case DCH_Y:
31363130
case DCH_I:
@@ -3141,7 +3135,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31413135
* 1-digit year: always +2000
31423136
*/
31433137
out->year += 2000;
3144-
s += SKIP_THth(n->suffix);
3138+
SKIP_THth(s, n->suffix);
31453139
break;
31463140
case DCH_RM:
31473141
from_char_seq_search(&value, &s, rm_months_upper,
@@ -3155,11 +3149,11 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31553149
break;
31563150
case DCH_W:
31573151
from_char_parse_int(&out->w, &s, n);
3158-
s += SKIP_THth(n->suffix);
3152+
SKIP_THth(s, n->suffix);
31593153
break;
31603154
case DCH_J:
31613155
from_char_parse_int(&out->j, &s, n);
3162-
s += SKIP_THth(n->suffix);
3156+
SKIP_THth(s, n->suffix);
31633157
break;
31643158
}
31653159
}

0 commit comments

Comments
 (0)
0