Skip to content

Commit e09651e

Browse files
committed
to_char(): prevent writing beyond the allocated buffer
Previously very long localized month and weekday strings could overflow the allocated buffers, causing a server crash. Reported and patch reviewed by Noah Misch. Backpatch to all supported versions. Security: CVE-2015-0241
1 parent 5ae3bf1 commit e09651e

File tree

1 file changed

+125
-14
lines changed

1 file changed

+125
-14
lines changed

src/backend/utils/adt/formatting.c

Lines changed: 125 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@
110110
* Maximal length of one node
111111
* ----------
112112
*/
113-
#define DCH_MAX_ITEM_SIZ 9 /* max julian day */
113+
#define DCH_MAX_ITEM_SIZ 12 /* max localized day name */
114114
#define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */
115115

116116
/* ----------
@@ -525,10 +525,12 @@ do { \
525525
* Suffixes definition for DATE-TIME TO/FROM CHAR
526526
* ----------
527527
*/
528+
#define TM_SUFFIX_LEN 2
529+
528530
static KeySuffix DCH_suff[] = {
529531
{"FM", 2, DCH_S_FM, SUFFTYPE_PREFIX},
530532
{"fm", 2, DCH_S_FM, SUFFTYPE_PREFIX},
531-
{"TM", 2, DCH_S_TM, SUFFTYPE_PREFIX},
533+
{"TM", TM_SUFFIX_LEN, DCH_S_TM, SUFFTYPE_PREFIX},
532534
{"tm", 2, DCH_S_TM, SUFFTYPE_PREFIX},
533535
{"TH", 2, DCH_S_TH, SUFFTYPE_POSTFIX},
534536
{"th", 2, DCH_S_th, SUFFTYPE_POSTFIX},
@@ -537,6 +539,7 @@ static KeySuffix DCH_suff[] = {
537539
{NULL, 0, 0, 0}
538540
};
539541

542+
540543
/* ----------
541544
* Format-pictures (KeyWord).
542545
*
@@ -2534,7 +2537,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
25342537
if (!tm->tm_mon)
25352538
break;
25362539
if (S_TM(n->suffix))
2537-
strcpy(s, str_toupper_z(localized_full_months[tm->tm_mon - 1], collid));
2540+
{
2541+
char *str = str_toupper_z(localized_full_months[tm->tm_mon - 1], collid);
2542+
2543+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2544+
strcpy(s, str);
2545+
else
2546+
ereport(ERROR,
2547+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2548+
errmsg("localized string format value too long")));
2549+
}
25382550
else
25392551
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
25402552
asc_toupper_z(months_full[tm->tm_mon - 1]));
@@ -2545,7 +2557,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
25452557
if (!tm->tm_mon)
25462558
break;
25472559
if (S_TM(n->suffix))
2548-
strcpy(s, str_initcap_z(localized_full_months[tm->tm_mon - 1], collid));
2560+
{
2561+
char *str = str_initcap_z(localized_full_months[tm->tm_mon - 1], collid);
2562+
2563+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2564+
strcpy(s, str);
2565+
else
2566+
ereport(ERROR,
2567+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2568+
errmsg("localized string format value too long")));
2569+
}
25492570
else
25502571
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
25512572
months_full[tm->tm_mon - 1]);
@@ -2556,7 +2577,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
25562577
if (!tm->tm_mon)
25572578
break;
25582579
if (S_TM(n->suffix))
2559-
strcpy(s, str_tolower_z(localized_full_months[tm->tm_mon - 1], collid));
2580+
{
2581+
char *str = str_tolower_z(localized_full_months[tm->tm_mon - 1], collid);
2582+
2583+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2584+
strcpy(s, str);
2585+
else
2586+
ereport(ERROR,
2587+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2588+
errmsg("localized string format value too long")));
2589+
}
25602590
else
25612591
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
25622592
asc_tolower_z(months_full[tm->tm_mon - 1]));
@@ -2567,7 +2597,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
25672597
if (!tm->tm_mon)
25682598
break;
25692599
if (S_TM(n->suffix))
2570-
strcpy(s, str_toupper_z(localized_abbrev_months[tm->tm_mon - 1], collid));
2600+
{
2601+
char *str = str_toupper_z(localized_abbrev_months[tm->tm_mon - 1], collid);
2602+
2603+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2604+
strcpy(s, str);
2605+
else
2606+
ereport(ERROR,
2607+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2608+
errmsg("localized string format value too long")));
2609+
}
25712610
else
25722611
strcpy(s, asc_toupper_z(months[tm->tm_mon - 1]));
25732612
s += strlen(s);
@@ -2577,7 +2616,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
25772616
if (!tm->tm_mon)
25782617
break;
25792618
if (S_TM(n->suffix))
2580-
strcpy(s, str_initcap_z(localized_abbrev_months[tm->tm_mon - 1], collid));
2619+
{
2620+
char *str = str_initcap_z(localized_abbrev_months[tm->tm_mon - 1], collid);
2621+
2622+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2623+
strcpy(s, str);
2624+
else
2625+
ereport(ERROR,
2626+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2627+
errmsg("localized string format value too long")));
2628+
}
25812629
else
25822630
strcpy(s, months[tm->tm_mon - 1]);
25832631
s += strlen(s);
@@ -2587,7 +2635,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
25872635
if (!tm->tm_mon)
25882636
break;
25892637
if (S_TM(n->suffix))
2590-
strcpy(s, str_tolower_z(localized_abbrev_months[tm->tm_mon - 1], collid));
2638+
{
2639+
char *str = str_tolower_z(localized_abbrev_months[tm->tm_mon - 1], collid);
2640+
2641+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2642+
strcpy(s, str);
2643+
else
2644+
ereport(ERROR,
2645+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2646+
errmsg("localized string format value too long")));
2647+
}
25912648
else
25922649
strcpy(s, asc_tolower_z(months[tm->tm_mon - 1]));
25932650
s += strlen(s);
@@ -2601,7 +2658,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
26012658
case DCH_DAY:
26022659
INVALID_FOR_INTERVAL;
26032660
if (S_TM(n->suffix))
2604-
strcpy(s, str_toupper_z(localized_full_days[tm->tm_wday], collid));
2661+
{
2662+
char *str = str_toupper_z(localized_full_days[tm->tm_wday], collid);
2663+
2664+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2665+
strcpy(s, str);
2666+
else
2667+
ereport(ERROR,
2668+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2669+
errmsg("localized string format value too long")));
2670+
}
26052671
else
26062672
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
26072673
asc_toupper_z(days[tm->tm_wday]));
@@ -2610,7 +2676,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
26102676
case DCH_Day:
26112677
INVALID_FOR_INTERVAL;
26122678
if (S_TM(n->suffix))
2613-
strcpy(s, str_initcap_z(localized_full_days[tm->tm_wday], collid));
2679+
{
2680+
char *str = str_initcap_z(localized_full_days[tm->tm_wday], collid);
2681+
2682+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2683+
strcpy(s, str);
2684+
else
2685+
ereport(ERROR,
2686+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2687+
errmsg("localized string format value too long")));
2688+
}
26142689
else
26152690
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
26162691
days[tm->tm_wday]);
@@ -2619,7 +2694,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
26192694
case DCH_day:
26202695
INVALID_FOR_INTERVAL;
26212696
if (S_TM(n->suffix))
2622-
strcpy(s, str_tolower_z(localized_full_days[tm->tm_wday], collid));
2697+
{
2698+
char *str = str_tolower_z(localized_full_days[tm->tm_wday], collid);
2699+
2700+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2701+
strcpy(s, str);
2702+
else
2703+
ereport(ERROR,
2704+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2705+
errmsg("localized string format value too long")));
2706+
}
26232707
else
26242708
sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9,
26252709
asc_tolower_z(days[tm->tm_wday]));
@@ -2628,23 +2712,50 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
26282712
case DCH_DY:
26292713
INVALID_FOR_INTERVAL;
26302714
if (S_TM(n->suffix))
2631-
strcpy(s, str_toupper_z(localized_abbrev_days[tm->tm_wday], collid));
2715+
{
2716+
char *str = str_toupper_z(localized_abbrev_days[tm->tm_wday], collid);
2717+
2718+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2719+
strcpy(s, str);
2720+
else
2721+
ereport(ERROR,
2722+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2723+
errmsg("localized string format value too long")));
2724+
}
26322725
else
26332726
strcpy(s, asc_toupper_z(days_short[tm->tm_wday]));
26342727
s += strlen(s);
26352728
break;
26362729
case DCH_Dy:
26372730
INVALID_FOR_INTERVAL;
26382731
if (S_TM(n->suffix))
2639-
strcpy(s, str_initcap_z(localized_abbrev_days[tm->tm_wday], collid));
2732+
{
2733+
char *str = str_initcap_z(localized_abbrev_days[tm->tm_wday], collid);
2734+
2735+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2736+
strcpy(s, str);
2737+
else
2738+
ereport(ERROR,
2739+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2740+
errmsg("localized string format value too long")));
2741+
}
26402742
else
26412743
strcpy(s, days_short[tm->tm_wday]);
26422744
s += strlen(s);
26432745
break;
26442746
case DCH_dy:
26452747
INVALID_FOR_INTERVAL;
26462748
if (S_TM(n->suffix))
2647-
strcpy(s, str_tolower_z(localized_abbrev_days[tm->tm_wday], collid));
2749+
{
2750+
char *str = str_tolower_z(localized_abbrev_days[tm->tm_wday], collid);
2751+
2752+
if (strlen(str) < (n->key->len + TM_SUFFIX_LEN) * DCH_MAX_ITEM_SIZ)
2753+
strcpy(s, str);
2754+
else
2755+
ereport(ERROR,
2756+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2757+
errmsg("localized string format value too long")));
2758+
}
26482759
else
26492760
strcpy(s, asc_tolower_z(days_short[tm->tm_wday]));
26502761
s += strlen(s);

0 commit comments

Comments
 (0)