Skip to content

Commit 462456d

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 d30c67a commit 462456d

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);
@@ -1981,9 +1980,19 @@ asc_toupper_z(const char *buff)
19811980

19821981
/* ----------
19831982
* Skip TM / th in FROM_CHAR
1983+
*
1984+
* If S_THth is on, skip two chars, assuming there are two available
19841985
* ----------
19851986
*/
1986-
#define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
1987+
#define SKIP_THth(ptr, _suf) \
1988+
do { \
1989+
if (S_THth(_suf)) \
1990+
{ \
1991+
if (*(ptr)) (ptr)++; \
1992+
if (*(ptr)) (ptr)++; \
1993+
} \
1994+
} while (0)
1995+
19871996

19881997
#ifdef DEBUG_TO_FROM_CHAR
19891998
/* -----------
@@ -2091,23 +2100,6 @@ strspace_len(char *str)
20912100
return len;
20922101
}
20932102

2094-
static int
2095-
strdigits_len(char *str)
2096-
{
2097-
char *p = str;
2098-
int len;
2099-
2100-
len = strspace_len(str);
2101-
p += len;
2102-
2103-
while (*p && isdigit((unsigned char) *p) && len <= DCH_MAX_ITEM_SIZ)
2104-
{
2105-
len++;
2106-
p++;
2107-
}
2108-
return len;
2109-
}
2110-
21112103
/*
21122104
* Set the date mode of a from-char conversion.
21132105
*
@@ -2985,19 +2977,19 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
29852977
case DCH_HH12:
29862978
from_char_parse_int_len(&out->hh, &s, 2, n);
29872979
out->clock = CLOCK_12_HOUR;
2988-
s += SKIP_THth(n->suffix);
2980+
SKIP_THth(s, n->suffix);
29892981
break;
29902982
case DCH_HH24:
29912983
from_char_parse_int_len(&out->hh, &s, 2, n);
2992-
s += SKIP_THth(n->suffix);
2984+
SKIP_THth(s, n->suffix);
29932985
break;
29942986
case DCH_MI:
29952987
from_char_parse_int(&out->mi, &s, n);
2996-
s += SKIP_THth(n->suffix);
2988+
SKIP_THth(s, n->suffix);
29972989
break;
29982990
case DCH_SS:
29992991
from_char_parse_int(&out->ss, &s, n);
3000-
s += SKIP_THth(n->suffix);
2992+
SKIP_THth(s, n->suffix);
30012993
break;
30022994
case DCH_MS: /* millisecond */
30032995
len = from_char_parse_int_len(&out->ms, &s, 3, n);
@@ -3008,7 +3000,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30083000
out->ms *= len == 1 ? 100 :
30093001
len == 2 ? 10 : 1;
30103002

3011-
s += SKIP_THth(n->suffix);
3003+
SKIP_THth(s, n->suffix);
30123004
break;
30133005
case DCH_US: /* microsecond */
30143006
len = from_char_parse_int_len(&out->us, &s, 6, n);
@@ -3019,11 +3011,11 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30193011
len == 4 ? 100 :
30203012
len == 5 ? 10 : 1;
30213013

3022-
s += SKIP_THth(n->suffix);
3014+
SKIP_THth(s, n->suffix);
30233015
break;
30243016
case DCH_SSSS:
30253017
from_char_parse_int(&out->ssss, &s, n);
3026-
s += SKIP_THth(n->suffix);
3018+
SKIP_THth(s, n->suffix);
30273019
break;
30283020
case DCH_tz:
30293021
case DCH_TZ:
@@ -3062,7 +3054,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30623054
break;
30633055
case DCH_MM:
30643056
from_char_parse_int(&out->mm, &s, n);
3065-
s += SKIP_THth(n->suffix);
3057+
SKIP_THth(s, n->suffix);
30663058
break;
30673059
case DCH_DAY:
30683060
case DCH_Day:
@@ -3082,31 +3074,31 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30823074
break;
30833075
case DCH_DDD:
30843076
from_char_parse_int(&out->ddd, &s, n);
3085-
s += SKIP_THth(n->suffix);
3077+
SKIP_THth(s, n->suffix);
30863078
break;
30873079
case DCH_IDDD:
30883080
from_char_parse_int_len(&out->ddd, &s, 3, n);
3089-
s += SKIP_THth(n->suffix);
3081+
SKIP_THth(s, n->suffix);
30903082
break;
30913083
case DCH_DD:
30923084
from_char_parse_int(&out->dd, &s, n);
3093-
s += SKIP_THth(n->suffix);
3085+
SKIP_THth(s, n->suffix);
30943086
break;
30953087
case DCH_D:
30963088
from_char_parse_int(&out->d, &s, n);
3097-
s += SKIP_THth(n->suffix);
3089+
SKIP_THth(s, n->suffix);
30983090
break;
30993091
case DCH_ID:
31003092
from_char_parse_int_len(&out->d, &s, 1, n);
31013093
/* Shift numbering to match Gregorian where Sunday = 1 */
31023094
if (++out->d > 7)
31033095
out->d = 1;
3104-
s += SKIP_THth(n->suffix);
3096+
SKIP_THth(s, n->suffix);
31053097
break;
31063098
case DCH_WW:
31073099
case DCH_IW:
31083100
from_char_parse_int(&out->ww, &s, n);
3109-
s += SKIP_THth(n->suffix);
3101+
SKIP_THth(s, n->suffix);
31103102
break;
31113103
case DCH_Q:
31123104

@@ -3121,55 +3113,57 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31213113
* isn't stored anywhere in 'out'.
31223114
*/
31233115
from_char_parse_int((int *) NULL, &s, n);
3124-
s += SKIP_THth(n->suffix);
3116+
SKIP_THth(s, n->suffix);
31253117
break;
31263118
case DCH_CC:
31273119
from_char_parse_int(&out->cc, &s, n);
3128-
s += SKIP_THth(n->suffix);
3120+
SKIP_THth(s, n->suffix);
31293121
break;
31303122
case DCH_Y_YYY:
31313123
{
31323124
int matched,
31333125
years,
3134-
millenia;
3126+
millenia,
3127+
nch;
31353128

3136-
matched = sscanf(s, "%d,%03d", &millenia, &years);
3137-
if (matched != 2)
3129+
matched = sscanf(s, "%d,%03d%n", &millenia, &years, &nch);
3130+
if (matched < 2)
31383131
ereport(ERROR,
31393132
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
31403133
errmsg("invalid input string for \"Y,YYY\"")));
31413134
years += (millenia * 1000);
31423135
from_char_set_int(&out->year, years, n);
31433136
out->yysz = 4;
3144-
s += strdigits_len(s) + 4 + SKIP_THth(n->suffix);
3137+
s += nch;
3138+
SKIP_THth(s, n->suffix);
31453139
}
31463140
break;
31473141
case DCH_YYYY:
31483142
case DCH_IYYY:
31493143
from_char_parse_int(&out->year, &s, n);
31503144
out->yysz = 4;
3151-
s += SKIP_THth(n->suffix);
3145+
SKIP_THth(s, n->suffix);
31523146
break;
31533147
case DCH_YYY:
31543148
case DCH_IYY:
31553149
if (from_char_parse_int(&out->year, &s, n) < 4)
31563150
out->year = adjust_partial_year_to_2020(out->year);
31573151
out->yysz = 3;
3158-
s += SKIP_THth(n->suffix);
3152+
SKIP_THth(s, n->suffix);
31593153
break;
31603154
case DCH_YY:
31613155
case DCH_IY:
31623156
if (from_char_parse_int(&out->year, &s, n) < 4)
31633157
out->year = adjust_partial_year_to_2020(out->year);
31643158
out->yysz = 2;
3165-
s += SKIP_THth(n->suffix);
3159+
SKIP_THth(s, n->suffix);
31663160
break;
31673161
case DCH_Y:
31683162
case DCH_I:
31693163
if (from_char_parse_int(&out->year, &s, n) < 4)
31703164
out->year = adjust_partial_year_to_2020(out->year);
31713165
out->yysz = 1;
3172-
s += SKIP_THth(n->suffix);
3166+
SKIP_THth(s, n->suffix);
31733167
break;
31743168
case DCH_RM:
31753169
from_char_seq_search(&value, &s, rm_months_upper,
@@ -3183,11 +3177,11 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31833177
break;
31843178
case DCH_W:
31853179
from_char_parse_int(&out->w, &s, n);
3186-
s += SKIP_THth(n->suffix);
3180+
SKIP_THth(s, n->suffix);
31873181
break;
31883182
case DCH_J:
31893183
from_char_parse_int(&out->j, &s, n);
3190-
s += SKIP_THth(n->suffix);
3184+
SKIP_THth(s, n->suffix);
31913185
break;
31923186
}
31933187
}

0 commit comments

Comments
 (0)