Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d136d60

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 6b8b4e4 commit d136d60

File tree

1 file changed

+39
-45
lines changed

1 file changed

+39
-45
lines changed

src/backend/utils/adt/formatting.c

+39-45
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
*
@@ -3007,19 +2999,19 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30072999
case DCH_HH12:
30083000
from_char_parse_int_len(&out->hh, &s, 2, n);
30093001
out->clock = CLOCK_12_HOUR;
3010-
s += SKIP_THth(n->suffix);
3002+
SKIP_THth(s, n->suffix);
30113003
break;
30123004
case DCH_HH24:
30133005
from_char_parse_int_len(&out->hh, &s, 2, n);
3014-
s += SKIP_THth(n->suffix);
3006+
SKIP_THth(s, n->suffix);
30153007
break;
30163008
case DCH_MI:
30173009
from_char_parse_int(&out->mi, &s, n);
3018-
s += SKIP_THth(n->suffix);
3010+
SKIP_THth(s, n->suffix);
30193011
break;
30203012
case DCH_SS:
30213013
from_char_parse_int(&out->ss, &s, n);
3022-
s += SKIP_THth(n->suffix);
3014+
SKIP_THth(s, n->suffix);
30233015
break;
30243016
case DCH_MS: /* millisecond */
30253017
len = from_char_parse_int_len(&out->ms, &s, 3, n);
@@ -3030,7 +3022,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30303022
out->ms *= len == 1 ? 100 :
30313023
len == 2 ? 10 : 1;
30323024

3033-
s += SKIP_THth(n->suffix);
3025+
SKIP_THth(s, n->suffix);
30343026
break;
30353027
case DCH_US: /* microsecond */
30363028
len = from_char_parse_int_len(&out->us, &s, 6, n);
@@ -3041,11 +3033,11 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30413033
len == 4 ? 100 :
30423034
len == 5 ? 10 : 1;
30433035

3044-
s += SKIP_THth(n->suffix);
3036+
SKIP_THth(s, n->suffix);
30453037
break;
30463038
case DCH_SSSS:
30473039
from_char_parse_int(&out->ssss, &s, n);
3048-
s += SKIP_THth(n->suffix);
3040+
SKIP_THth(s, n->suffix);
30493041
break;
30503042
case DCH_tz:
30513043
case DCH_TZ:
@@ -3085,7 +3077,7 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30853077
break;
30863078
case DCH_MM:
30873079
from_char_parse_int(&out->mm, &s, n);
3088-
s += SKIP_THth(n->suffix);
3080+
SKIP_THth(s, n->suffix);
30893081
break;
30903082
case DCH_DAY:
30913083
case DCH_Day:
@@ -3105,31 +3097,31 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31053097
break;
31063098
case DCH_DDD:
31073099
from_char_parse_int(&out->ddd, &s, n);
3108-
s += SKIP_THth(n->suffix);
3100+
SKIP_THth(s, n->suffix);
31093101
break;
31103102
case DCH_IDDD:
31113103
from_char_parse_int_len(&out->ddd, &s, 3, n);
3112-
s += SKIP_THth(n->suffix);
3104+
SKIP_THth(s, n->suffix);
31133105
break;
31143106
case DCH_DD:
31153107
from_char_parse_int(&out->dd, &s, n);
3116-
s += SKIP_THth(n->suffix);
3108+
SKIP_THth(s, n->suffix);
31173109
break;
31183110
case DCH_D:
31193111
from_char_parse_int(&out->d, &s, n);
3120-
s += SKIP_THth(n->suffix);
3112+
SKIP_THth(s, n->suffix);
31213113
break;
31223114
case DCH_ID:
31233115
from_char_parse_int_len(&out->d, &s, 1, n);
31243116
/* Shift numbering to match Gregorian where Sunday = 1 */
31253117
if (++out->d > 7)
31263118
out->d = 1;
3127-
s += SKIP_THth(n->suffix);
3119+
SKIP_THth(s, n->suffix);
31283120
break;
31293121
case DCH_WW:
31303122
case DCH_IW:
31313123
from_char_parse_int(&out->ww, &s, n);
3132-
s += SKIP_THth(n->suffix);
3124+
SKIP_THth(s, n->suffix);
31333125
break;
31343126
case DCH_Q:
31353127

@@ -3144,55 +3136,57 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
31443136
* isn't stored anywhere in 'out'.
31453137
*/
31463138
from_char_parse_int((int *) NULL, &s, n);
3147-
s += SKIP_THth(n->suffix);
3139+
SKIP_THth(s, n->suffix);
31483140
break;
31493141
case DCH_CC:
31503142
from_char_parse_int(&out->cc, &s, n);
3151-
s += SKIP_THth(n->suffix);
3143+
SKIP_THth(s, n->suffix);
31523144
break;
31533145
case DCH_Y_YYY:
31543146
{
31553147
int matched,
31563148
years,
3157-
millenia;
3149+
millenia,
3150+
nch;
31583151

3159-
matched = sscanf(s, "%d,%03d", &millenia, &years);
3160-
if (matched != 2)
3152+
matched = sscanf(s, "%d,%03d%n", &millenia, &years, &nch);
3153+
if (matched < 2)
31613154
ereport(ERROR,
31623155
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
31633156
errmsg("invalid input string for \"Y,YYY\"")));
31643157
years += (millenia * 1000);
31653158
from_char_set_int(&out->year, years, n);
31663159
out->yysz = 4;
3167-
s += strdigits_len(s) + 4 + SKIP_THth(n->suffix);
3160+
s += nch;
3161+
SKIP_THth(s, n->suffix);
31683162
}
31693163
break;
31703164
case DCH_YYYY:
31713165
case DCH_IYYY:
31723166
from_char_parse_int(&out->year, &s, n);
31733167
out->yysz = 4;
3174-
s += SKIP_THth(n->suffix);
3168+
SKIP_THth(s, n->suffix);
31753169
break;
31763170
case DCH_YYY:
31773171
case DCH_IYY:
31783172
if (from_char_parse_int(&out->year, &s, n) < 4)
31793173
out->year = adjust_partial_year_to_2020(out->year);
31803174
out->yysz = 3;
3181-
s += SKIP_THth(n->suffix);
3175+
SKIP_THth(s, n->suffix);
31823176
break;
31833177
case DCH_YY:
31843178
case DCH_IY:
31853179
if (from_char_parse_int(&out->year, &s, n) < 4)
31863180
out->year = adjust_partial_year_to_2020(out->year);
31873181
out->yysz = 2;
3188-
s += SKIP_THth(n->suffix);
3182+
SKIP_THth(s, n->suffix);
31893183
break;
31903184
case DCH_Y:
31913185
case DCH_I:
31923186
if (from_char_parse_int(&out->year, &s, n) < 4)
31933187
out->year = adjust_partial_year_to_2020(out->year);
31943188
out->yysz = 1;
3195-
s += SKIP_THth(n->suffix);
3189+
SKIP_THth(s, n->suffix);
31963190
break;
31973191
case DCH_RM:
31983192
from_char_seq_search(&value, &s, rm_months_upper,
@@ -3206,11 +3200,11 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
32063200
break;
32073201
case DCH_W:
32083202
from_char_parse_int(&out->w, &s, n);
3209-
s += SKIP_THth(n->suffix);
3203+
SKIP_THth(s, n->suffix);
32103204
break;
32113205
case DCH_J:
32123206
from_char_parse_int(&out->j, &s, n);
3213-
s += SKIP_THth(n->suffix);
3207+
SKIP_THth(s, n->suffix);
32143208
break;
32153209
}
32163210
}

0 commit comments

Comments
 (0)