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

Skip to content

Commit 11247dd

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 29d154e commit 11247dd

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
@@ -969,7 +969,6 @@ static char *get_th(char *num, int type);
969969
static char *str_numth(char *dest, char *num, int type);
970970
static int adjust_partial_year_to_2020(int year);
971971
static int strspace_len(char *str);
972-
static int strdigits_len(char *str);
973972
static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode);
974973
static void from_char_set_int(int *dest, const int value, const FormatNode *node);
975974
static int from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node);
@@ -1983,9 +1982,19 @@ asc_toupper_z(const char *buff)
19831982

19841983
/* ----------
19851984
* Skip TM / th in FROM_CHAR
1985+
*
1986+
* If S_THth is on, skip two chars, assuming there are two available
19861987
* ----------
19871988
*/
1988-
#define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
1989+
#define SKIP_THth(ptr, _suf) \
1990+
do { \
1991+
if (S_THth(_suf)) \
1992+
{ \
1993+
if (*(ptr)) (ptr)++; \
1994+
if (*(ptr)) (ptr)++; \
1995+
} \
1996+
} while (0)
1997+
19891998

19901999
#ifdef DEBUG_TO_FROM_CHAR
19912000
/* -----------
@@ -2093,23 +2102,6 @@ strspace_len(char *str)
20932102
return len;
20942103
}
20952104

2096-
static int
2097-
strdigits_len(char *str)
2098-
{
2099-
char *p = str;
2100-
int len;
2101-
2102-
len = strspace_len(str);
2103-
p += len;
2104-
2105-
while (*p && isdigit((unsigned char) *p) && len <= DCH_MAX_ITEM_SIZ)
2106-
{
2107-
len++;
2108-
p++;
2109-
}
2110-
return len;
2111-
}
2112-
21132105
/*
21142106
* Set the date mode of a from-char conversion.
21152107
*
@@ -2980,19 +2972,19 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
29802972
case DCH_HH12:
29812973
from_char_parse_int_len(&out->hh, &s, 2, n);
29822974
out->clock = CLOCK_12_HOUR;
2983-
s += SKIP_THth(n->suffix);
2975+
SKIP_THth(s, n->suffix);
29842976
break;
29852977
case DCH_HH24:
29862978
from_char_parse_int_len(&out->hh, &s, 2, n);
2987-
s += SKIP_THth(n->suffix);
2979+
SKIP_THth(s, n->suffix);
29882980
break;
29892981
case DCH_MI:
29902982
from_char_parse_int(&out->mi, &s, n);
2991-
s += SKIP_THth(n->suffix);
2983+
SKIP_THth(s, n->suffix);
29922984
break;
29932985
case DCH_SS:
29942986
from_char_parse_int(&out->ss, &s, n);
2995-
s += SKIP_THth(n->suffix);
2987+
SKIP_THth(s, n->suffix);
29962988
break;
29972989
case DCH_MS: /* millisecond */
29982990
len = from_char_parse_int_len(&out->ms, &s, 3, n);
@@ -3003,7 +2995,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30032995
out->ms *= len == 1 ? 100 :
30042996
len == 2 ? 10 : 1;
30052997

3006-
s += SKIP_THth(n->suffix);
2998+
SKIP_THth(s, n->suffix);
30072999
break;
30083000
case DCH_US: /* microsecond */
30093001
len = from_char_parse_int_len(&out->us, &s, 6, n);
@@ -3014,11 +3006,11 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30143006
len == 4 ? 100 :
30153007
len == 5 ? 10 : 1;
30163008

3017-
s += SKIP_THth(n->suffix);
3009+
SKIP_THth(s, n->suffix);
30183010
break;
30193011
case DCH_SSSS:
30203012
from_char_parse_int(&out->ssss, &s, n);
3021-
s += SKIP_THth(n->suffix);
3013+
SKIP_THth(s, n->suffix);
30223014
break;
30233015
case DCH_tz:
30243016
case DCH_TZ:
@@ -3057,7 +3049,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30573049
break;
30583050
case DCH_MM:
30593051
from_char_parse_int(&out->mm, &s, n);
3060-
s += SKIP_THth(n->suffix);
3052+
SKIP_THth(s, n->suffix);
30613053
break;
30623054
case DCH_DAY:
30633055
case DCH_Day:
@@ -3075,29 +3067,29 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30753067
break;
30763068
case DCH_DDD:
30773069
from_char_parse_int(&out->ddd, &s, n);
3078-
s += SKIP_THth(n->suffix);
3070+
SKIP_THth(s, n->suffix);
30793071
break;
30803072
case DCH_IDDD:
30813073
from_char_parse_int_len(&out->ddd, &s, 3, n);
3082-
s += SKIP_THth(n->suffix);
3074+
SKIP_THth(s, n->suffix);
30833075
break;
30843076
case DCH_DD:
30853077
from_char_parse_int(&out->dd, &s, n);
3086-
s += SKIP_THth(n->suffix);
3078+
SKIP_THth(s, n->suffix);
30873079
break;
30883080
case DCH_D:
30893081
from_char_parse_int(&out->d, &s, n);
30903082
out->d--;
3091-
s += SKIP_THth(n->suffix);
3083+
SKIP_THth(s, n->suffix);
30923084
break;
30933085
case DCH_ID:
30943086
from_char_parse_int_len(&out->d, &s, 1, n);
3095-
s += SKIP_THth(n->suffix);
3087+
SKIP_THth(s, n->suffix);
30963088
break;
30973089
case DCH_WW:
30983090
case DCH_IW:
30993091
from_char_parse_int(&out->ww, &s, n);
3100-
s += SKIP_THth(n->suffix);
3092+
SKIP_THth(s, n->suffix);
31013093
break;
31023094
case DCH_Q:
31033095

@@ -3112,55 +3104,57 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31123104
* isn't stored anywhere in 'out'.
31133105
*/
31143106
from_char_parse_int((int *) NULL, &s, n);
3115-
s += SKIP_THth(n->suffix);
3107+
SKIP_THth(s, n->suffix);
31163108
break;
31173109
case DCH_CC:
31183110
from_char_parse_int(&out->cc, &s, n);
3119-
s += SKIP_THth(n->suffix);
3111+
SKIP_THth(s, n->suffix);
31203112
break;
31213113
case DCH_Y_YYY:
31223114
{
31233115
int matched,
31243116
years,
3125-
millenia;
3117+
millenia,
3118+
nch;
31263119

3127-
matched = sscanf(s, "%d,%03d", &millenia, &years);
3128-
if (matched != 2)
3120+
matched = sscanf(s, "%d,%03d%n", &millenia, &years, &nch);
3121+
if (matched < 2)
31293122
ereport(ERROR,
31303123
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
31313124
errmsg("invalid input string for \"Y,YYY\"")));
31323125
years += (millenia * 1000);
31333126
from_char_set_int(&out->year, years, n);
31343127
out->yysz = 4;
3135-
s += strdigits_len(s) + 4 + SKIP_THth(n->suffix);
3128+
s += nch;
3129+
SKIP_THth(s, n->suffix);
31363130
}
31373131
break;
31383132
case DCH_YYYY:
31393133
case DCH_IYYY:
31403134
from_char_parse_int(&out->year, &s, n);
31413135
out->yysz = 4;
3142-
s += SKIP_THth(n->suffix);
3136+
SKIP_THth(s, n->suffix);
31433137
break;
31443138
case DCH_YYY:
31453139
case DCH_IYY:
31463140
if (from_char_parse_int(&out->year, &s, n) < 4)
31473141
out->year = adjust_partial_year_to_2020(out->year);
31483142
out->yysz = 3;
3149-
s += SKIP_THth(n->suffix);
3143+
SKIP_THth(s, n->suffix);
31503144
break;
31513145
case DCH_YY:
31523146
case DCH_IY:
31533147
if (from_char_parse_int(&out->year, &s, n) < 4)
31543148
out->year = adjust_partial_year_to_2020(out->year);
31553149
out->yysz = 2;
3156-
s += SKIP_THth(n->suffix);
3150+
SKIP_THth(s, n->suffix);
31573151
break;
31583152
case DCH_Y:
31593153
case DCH_I:
31603154
if (from_char_parse_int(&out->year, &s, n) < 4)
31613155
out->year = adjust_partial_year_to_2020(out->year);
31623156
out->yysz = 1;
3163-
s += SKIP_THth(n->suffix);
3157+
SKIP_THth(s, n->suffix);
31643158
break;
31653159
case DCH_RM:
31663160
from_char_seq_search(&value, &s, rm_months_upper,
@@ -3174,11 +3168,11 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31743168
break;
31753169
case DCH_W:
31763170
from_char_parse_int(&out->w, &s, n);
3177-
s += SKIP_THth(n->suffix);
3171+
SKIP_THth(s, n->suffix);
31783172
break;
31793173
case DCH_J:
31803174
from_char_parse_int(&out->j, &s, n);
3181-
s += SKIP_THth(n->suffix);
3175+
SKIP_THth(s, n->suffix);
31823176
break;
31833177
}
31843178
}

0 commit comments

Comments
 (0)
0