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

Skip to content

Commit 1180868

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 a5148e8 commit 1180868

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
@@ -964,7 +964,6 @@ static const char *get_th(char *num, int type);
964964
static char *str_numth(char *dest, char *num, int type);
965965
static int adjust_partial_year_to_2020(int year);
966966
static int strspace_len(char *str);
967-
static int strdigits_len(char *str);
968967
static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode);
969968
static void from_char_set_int(int *dest, const int value, const FormatNode *node);
970969
static int from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node);
@@ -1976,9 +1975,19 @@ asc_toupper_z(const char *buff)
19761975

19771976
/* ----------
19781977
* Skip TM / th in FROM_CHAR
1978+
*
1979+
* If S_THth is on, skip two chars, assuming there are two available
19791980
* ----------
19801981
*/
1981-
#define SKIP_THth(_suf) (S_THth(_suf) ? 2 : 0)
1982+
#define SKIP_THth(ptr, _suf) \
1983+
do { \
1984+
if (S_THth(_suf)) \
1985+
{ \
1986+
if (*(ptr)) (ptr)++; \
1987+
if (*(ptr)) (ptr)++; \
1988+
} \
1989+
} while (0)
1990+
19821991

19831992
#ifdef DEBUG_TO_FROM_CHAR
19841993
/* -----------
@@ -2086,23 +2095,6 @@ strspace_len(char *str)
20862095
return len;
20872096
}
20882097

2089-
static int
2090-
strdigits_len(char *str)
2091-
{
2092-
char *p = str;
2093-
int len;
2094-
2095-
len = strspace_len(str);
2096-
p += len;
2097-
2098-
while (*p && isdigit((unsigned char) *p) && len <= DCH_MAX_ITEM_SIZ)
2099-
{
2100-
len++;
2101-
p++;
2102-
}
2103-
return len;
2104-
}
2105-
21062098
/*
21072099
* Set the date mode of a from-char conversion.
21082100
*
@@ -3000,19 +2992,19 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30002992
case DCH_HH12:
30012993
from_char_parse_int_len(&out->hh, &s, 2, n);
30022994
out->clock = CLOCK_12_HOUR;
3003-
s += SKIP_THth(n->suffix);
2995+
SKIP_THth(s, n->suffix);
30042996
break;
30052997
case DCH_HH24:
30062998
from_char_parse_int_len(&out->hh, &s, 2, n);
3007-
s += SKIP_THth(n->suffix);
2999+
SKIP_THth(s, n->suffix);
30083000
break;
30093001
case DCH_MI:
30103002
from_char_parse_int(&out->mi, &s, n);
3011-
s += SKIP_THth(n->suffix);
3003+
SKIP_THth(s, n->suffix);
30123004
break;
30133005
case DCH_SS:
30143006
from_char_parse_int(&out->ss, &s, n);
3015-
s += SKIP_THth(n->suffix);
3007+
SKIP_THth(s, n->suffix);
30163008
break;
30173009
case DCH_MS: /* millisecond */
30183010
len = from_char_parse_int_len(&out->ms, &s, 3, n);
@@ -3023,7 +3015,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30233015
out->ms *= len == 1 ? 100 :
30243016
len == 2 ? 10 : 1;
30253017

3026-
s += SKIP_THth(n->suffix);
3018+
SKIP_THth(s, n->suffix);
30273019
break;
30283020
case DCH_US: /* microsecond */
30293021
len = from_char_parse_int_len(&out->us, &s, 6, n);
@@ -3034,11 +3026,11 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30343026
len == 4 ? 100 :
30353027
len == 5 ? 10 : 1;
30363028

3037-
s += SKIP_THth(n->suffix);
3029+
SKIP_THth(s, n->suffix);
30383030
break;
30393031
case DCH_SSSS:
30403032
from_char_parse_int(&out->ssss, &s, n);
3041-
s += SKIP_THth(n->suffix);
3033+
SKIP_THth(s, n->suffix);
30423034
break;
30433035
case DCH_tz:
30443036
case DCH_TZ:
@@ -3078,7 +3070,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30783070
break;
30793071
case DCH_MM:
30803072
from_char_parse_int(&out->mm, &s, n);
3081-
s += SKIP_THth(n->suffix);
3073+
SKIP_THth(s, n->suffix);
30823074
break;
30833075
case DCH_DAY:
30843076
case DCH_Day:
@@ -3098,31 +3090,31 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30983090
break;
30993091
case DCH_DDD:
31003092
from_char_parse_int(&out->ddd, &s, n);
3101-
s += SKIP_THth(n->suffix);
3093+
SKIP_THth(s, n->suffix);
31023094
break;
31033095
case DCH_IDDD:
31043096
from_char_parse_int_len(&out->ddd, &s, 3, n);
3105-
s += SKIP_THth(n->suffix);
3097+
SKIP_THth(s, n->suffix);
31063098
break;
31073099
case DCH_DD:
31083100
from_char_parse_int(&out->dd, &s, n);
3109-
s += SKIP_THth(n->suffix);
3101+
SKIP_THth(s, n->suffix);
31103102
break;
31113103
case DCH_D:
31123104
from_char_parse_int(&out->d, &s, n);
3113-
s += SKIP_THth(n->suffix);
3105+
SKIP_THth(s, n->suffix);
31143106
break;
31153107
case DCH_ID:
31163108
from_char_parse_int_len(&out->d, &s, 1, n);
31173109
/* Shift numbering to match Gregorian where Sunday = 1 */
31183110
if (++out->d > 7)
31193111
out->d = 1;
3120-
s += SKIP_THth(n->suffix);
3112+
SKIP_THth(s, n->suffix);
31213113
break;
31223114
case DCH_WW:
31233115
case DCH_IW:
31243116
from_char_parse_int(&out->ww, &s, n);
3125-
s += SKIP_THth(n->suffix);
3117+
SKIP_THth(s, n->suffix);
31263118
break;
31273119
case DCH_Q:
31283120

@@ -3137,55 +3129,57 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31373129
* isn't stored anywhere in 'out'.
31383130
*/
31393131
from_char_parse_int((int *) NULL, &s, n);
3140-
s += SKIP_THth(n->suffix);
3132+
SKIP_THth(s, n->suffix);
31413133
break;
31423134
case DCH_CC:
31433135
from_char_parse_int(&out->cc, &s, n);
3144-
s += SKIP_THth(n->suffix);
3136+
SKIP_THth(s, n->suffix);
31453137
break;
31463138
case DCH_Y_YYY:
31473139
{
31483140
int matched,
31493141
years,
3150-
millenia;
3142+
millenia,
3143+
nch;
31513144

3152-
matched = sscanf(s, "%d,%03d", &millenia, &years);
3153-
if (matched != 2)
3145+
matched = sscanf(s, "%d,%03d%n", &millenia, &years, &nch);
3146+
if (matched < 2)
31543147
ereport(ERROR,
31553148
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
31563149
errmsg("invalid input string for \"Y,YYY\"")));
31573150
years += (millenia * 1000);
31583151
from_char_set_int(&out->year, years, n);
31593152
out->yysz = 4;
3160-
s += strdigits_len(s) + 4 + SKIP_THth(n->suffix);
3153+
s += nch;
3154+
SKIP_THth(s, n->suffix);
31613155
}
31623156
break;
31633157
case DCH_YYYY:
31643158
case DCH_IYYY:
31653159
from_char_parse_int(&out->year, &s, n);
31663160
out->yysz = 4;
3167-
s += SKIP_THth(n->suffix);
3161+
SKIP_THth(s, n->suffix);
31683162
break;
31693163
case DCH_YYY:
31703164
case DCH_IYY:
31713165
if (from_char_parse_int(&out->year, &s, n) < 4)
31723166
out->year = adjust_partial_year_to_2020(out->year);
31733167
out->yysz = 3;
3174-
s += SKIP_THth(n->suffix);
3168+
SKIP_THth(s, n->suffix);
31753169
break;
31763170
case DCH_YY:
31773171
case DCH_IY:
31783172
if (from_char_parse_int(&out->year, &s, n) < 4)
31793173
out->year = adjust_partial_year_to_2020(out->year);
31803174
out->yysz = 2;
3181-
s += SKIP_THth(n->suffix);
3175+
SKIP_THth(s, n->suffix);
31823176
break;
31833177
case DCH_Y:
31843178
case DCH_I:
31853179
if (from_char_parse_int(&out->year, &s, n) < 4)
31863180
out->year = adjust_partial_year_to_2020(out->year);
31873181
out->yysz = 1;
3188-
s += SKIP_THth(n->suffix);
3182+
SKIP_THth(s, n->suffix);
31893183
break;
31903184
case DCH_RM:
31913185
from_char_seq_search(&value, &s, rm_months_upper,
@@ -3199,11 +3193,11 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31993193
break;
32003194
case DCH_W:
32013195
from_char_parse_int(&out->w, &s, n);
3202-
s += SKIP_THth(n->suffix);
3196+
SKIP_THth(s, n->suffix);
32033197
break;
32043198
case DCH_J:
32053199
from_char_parse_int(&out->j, &s, n);
3206-
s += SKIP_THth(n->suffix);
3200+
SKIP_THth(s, n->suffix);
32073201
break;
32083202
}
32093203
}

0 commit comments

Comments
 (0)
0