Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita4930e7

Browse files
committed
Fix PGLC_localeconv() to handle errors better.
The code was intentionally not very careful about leaking strdup'dstrings in case of an error. That was forgivable probably, but italso failed to notice strdup() failures, which could lead to subsequentnull-pointer-dereference crashes, since many callers unsurprisinglydidn't check for null pointers in the struct lconv fields. An evenworse problem is that it could throw error while we were setlocale'dto a non-C locale, causing unwanted behavior in subsequent libc calls.Rewrite to ensure that we cannot throw elog(ERROR) until after we'verestored the previous locale settings, or at least attempted to.(I'm sorely tempted to make restore failure be a FATAL error, butwill refrain for the moment.) Having done that, it's not much morework to ensure that we clean up strdup'd storage on the way out, too.This code is substantially the same in all supported branches, soback-patch all the way.Michael Paquier and Tom LaneDiscussion: <CAB7nPqRMbGqa_mesopcn4MPyTs34eqtVEK7ELYxvvV=oqS00YA@mail.gmail.com>
1 parent4324ade commita4930e7

File tree

1 file changed

+164
-52
lines changed

1 file changed

+164
-52
lines changed

‎src/backend/utils/adt/pg_locale.c

Lines changed: 164 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -382,51 +382,91 @@ assign_locale_messages(const char *newval, void *extra)
382382

383383
/*
384384
* Frees the malloced content of a struct lconv. (But not the struct
385-
* itself.)
385+
* itself.) It's important that this not throw elog(ERROR).
386386
*/
387387
staticvoid
388388
free_struct_lconv(structlconv*s)
389389
{
390-
if (s->currency_symbol)
391-
free(s->currency_symbol);
392390
if (s->decimal_point)
393391
free(s->decimal_point);
394-
if (s->grouping)
395-
free(s->grouping);
396392
if (s->thousands_sep)
397393
free(s->thousands_sep);
394+
if (s->grouping)
395+
free(s->grouping);
398396
if (s->int_curr_symbol)
399397
free(s->int_curr_symbol);
398+
if (s->currency_symbol)
399+
free(s->currency_symbol);
400400
if (s->mon_decimal_point)
401401
free(s->mon_decimal_point);
402-
if (s->mon_grouping)
403-
free(s->mon_grouping);
404402
if (s->mon_thousands_sep)
405403
free(s->mon_thousands_sep);
406-
if (s->negative_sign)
407-
free(s->negative_sign);
404+
if (s->mon_grouping)
405+
free(s->mon_grouping);
408406
if (s->positive_sign)
409407
free(s->positive_sign);
408+
if (s->negative_sign)
409+
free(s->negative_sign);
410+
}
411+
412+
/*
413+
* Check that all fields of a struct lconv (or at least, the ones we care
414+
* about) are non-NULL. The field list must match free_struct_lconv().
415+
*/
416+
staticbool
417+
struct_lconv_is_valid(structlconv*s)
418+
{
419+
if (s->decimal_point==NULL)
420+
return false;
421+
if (s->thousands_sep==NULL)
422+
return false;
423+
if (s->grouping==NULL)
424+
return false;
425+
if (s->int_curr_symbol==NULL)
426+
return false;
427+
if (s->currency_symbol==NULL)
428+
return false;
429+
if (s->mon_decimal_point==NULL)
430+
return false;
431+
if (s->mon_thousands_sep==NULL)
432+
return false;
433+
if (s->mon_grouping==NULL)
434+
return false;
435+
if (s->positive_sign==NULL)
436+
return false;
437+
if (s->negative_sign==NULL)
438+
return false;
439+
return true;
410440
}
411441

412442

413443
/*
414-
*Return a strdup'ed stringconverted from the specified encoding to the
444+
*Convert the strdup'd stringat *str from the specified encoding to the
415445
* database encoding.
416446
*/
417-
staticchar*
418-
db_encoding_strdup(intencoding,constchar*str)
447+
staticvoid
448+
db_encoding_convert(intencoding,char**str)
419449
{
420450
char*pstr;
421451
char*mstr;
422452

423453
/* convert the string to the database encoding */
424-
pstr=pg_any_to_server(str,strlen(str),encoding);
454+
pstr=pg_any_to_server(*str,strlen(*str),encoding);
455+
if (pstr==*str)
456+
return;/* no conversion happened */
457+
458+
/* need it malloc'd not palloc'd */
425459
mstr=strdup(pstr);
426-
if (pstr!=str)
427-
pfree(pstr);
460+
if (mstr==NULL)
461+
ereport(ERROR,
462+
(errcode(ERRCODE_OUT_OF_MEMORY),
463+
errmsg("out of memory")));
464+
465+
/* replace old string */
466+
free(*str);
467+
*str=mstr;
428468

429-
returnmstr;
469+
pfree(pstr);
430470
}
431471

432472

@@ -440,13 +480,10 @@ PGLC_localeconv(void)
440480
staticstructlconvCurrentLocaleConv;
441481
staticboolCurrentLocaleConvAllocated= false;
442482
structlconv*extlconv;
483+
structlconvworklconv;
484+
booltrouble= false;
443485
char*save_lc_monetary;
444486
char*save_lc_numeric;
445-
char*decimal_point;
446-
char*grouping;
447-
char*thousands_sep;
448-
intencoding;
449-
450487
#ifdefWIN32
451488
char*save_lc_ctype;
452489
#endif
@@ -462,6 +499,20 @@ PGLC_localeconv(void)
462499
CurrentLocaleConvAllocated= false;
463500
}
464501

502+
/*
503+
* This is tricky because we really don't want to risk throwing error
504+
* while the locale is set to other than our usual settings. Therefore,
505+
* the process is: collect the usual settings, set locale to special
506+
* setting, copy relevant data into worklconv using strdup(), restore
507+
* normal settings, convert data to desired encoding, and finally stash
508+
* the collected data in CurrentLocaleConv. This makes it safe if we
509+
* throw an error during encoding conversion or run out of memory anywhere
510+
* in the process. All data pointed to by struct lconv members is
511+
* allocated with strdup, to avoid premature elog(ERROR) and to allow
512+
* using a single cleanup routine.
513+
*/
514+
memset(&worklconv,0,sizeof(worklconv));
515+
465516
/* Save user's values of monetary and numeric locales */
466517
save_lc_monetary=setlocale(LC_MONETARY,NULL);
467518
if (save_lc_monetary)
@@ -477,7 +528,7 @@ PGLC_localeconv(void)
477528
* Ideally, monetary and numeric local symbols could be returned in any
478529
* server encoding. Unfortunately, the WIN32 API does not allow
479530
* setlocale() to return values in a codepage/CTYPE that uses more than
480-
* two bytes per character,like UTF-8:
531+
* two bytes per character,such as UTF-8:
481532
*
482533
* http://msdn.microsoft.com/en-us/library/x99tb11d.aspx
483534
*
@@ -487,7 +538,7 @@ PGLC_localeconv(void)
487538
*
488539
* Therefore, we set LC_CTYPE to match LC_NUMERIC or LC_MONETARY (which
489540
* cannot be UTF8), call localeconv(), and then convert from the
490-
* numeric/monitary LC_CTYPE to the server encoding. One example use of
541+
* numeric/monetary LC_CTYPE to the server encoding. One example use of
491542
* this is for the Euro symbol.
492543
*
493544
* Perhaps someday we will use GetLocaleInfoW() which returns values in
@@ -499,18 +550,20 @@ PGLC_localeconv(void)
499550
if (save_lc_ctype)
500551
save_lc_ctype=pstrdup(save_lc_ctype);
501552

553+
/* Here begins the critical section where we must not throw error */
554+
502555
/* use numeric to set the ctype */
503556
setlocale(LC_CTYPE,locale_numeric);
504557
#endif
505558

506559
/* Get formatting information for numeric */
507560
setlocale(LC_NUMERIC,locale_numeric);
508561
extlconv=localeconv();
509-
encoding=pg_get_encoding_from_locale(locale_numeric, true);
510562

511-
decimal_point=db_encoding_strdup(encoding,extlconv->decimal_point);
512-
thousands_sep=db_encoding_strdup(encoding,extlconv->thousands_sep);
513-
grouping=strdup(extlconv->grouping);
563+
/* Must copy data now in case setlocale() overwrites it */
564+
worklconv.decimal_point=strdup(extlconv->decimal_point);
565+
worklconv.thousands_sep=strdup(extlconv->thousands_sep);
566+
worklconv.grouping=strdup(extlconv->grouping);
514567

515568
#ifdefWIN32
516569
/* use monetary to set the ctype */
@@ -520,52 +573,111 @@ PGLC_localeconv(void)
520573
/* Get formatting information for monetary */
521574
setlocale(LC_MONETARY,locale_monetary);
522575
extlconv=localeconv();
523-
encoding=pg_get_encoding_from_locale(locale_monetary, true);
524576

525-
/*
526-
* Must copy all values since restoring internal settings may overwrite
527-
* localeconv()'s results. Note that if we were to fail within this
528-
* sequence before reaching "CurrentLocaleConvAllocated = true", we could
529-
* leak some memory --- but not much, so it's not worth agonizing over.
530-
*/
531-
CurrentLocaleConv=*extlconv;
532-
CurrentLocaleConv.decimal_point=decimal_point;
533-
CurrentLocaleConv.grouping=grouping;
534-
CurrentLocaleConv.thousands_sep=thousands_sep;
535-
CurrentLocaleConv.int_curr_symbol=db_encoding_strdup(encoding,extlconv->int_curr_symbol);
536-
CurrentLocaleConv.currency_symbol=db_encoding_strdup(encoding,extlconv->currency_symbol);
537-
CurrentLocaleConv.mon_decimal_point=db_encoding_strdup(encoding,extlconv->mon_decimal_point);
538-
CurrentLocaleConv.mon_grouping=strdup(extlconv->mon_grouping);
539-
CurrentLocaleConv.mon_thousands_sep=db_encoding_strdup(encoding,extlconv->mon_thousands_sep);
540-
CurrentLocaleConv.negative_sign=db_encoding_strdup(encoding,extlconv->negative_sign);
541-
CurrentLocaleConv.positive_sign=db_encoding_strdup(encoding,extlconv->positive_sign);
542-
CurrentLocaleConvAllocated= true;
577+
/* Must copy data now in case setlocale() overwrites it */
578+
worklconv.int_curr_symbol=strdup(extlconv->int_curr_symbol);
579+
worklconv.currency_symbol=strdup(extlconv->currency_symbol);
580+
worklconv.mon_decimal_point=strdup(extlconv->mon_decimal_point);
581+
worklconv.mon_thousands_sep=strdup(extlconv->mon_thousands_sep);
582+
worklconv.mon_grouping=strdup(extlconv->mon_grouping);
583+
worklconv.positive_sign=strdup(extlconv->positive_sign);
584+
worklconv.negative_sign=strdup(extlconv->negative_sign);
585+
/* Copy scalar fields as well */
586+
worklconv.int_frac_digits=extlconv->int_frac_digits;
587+
worklconv.frac_digits=extlconv->frac_digits;
588+
worklconv.p_cs_precedes=extlconv->p_cs_precedes;
589+
worklconv.p_sep_by_space=extlconv->p_sep_by_space;
590+
worklconv.n_cs_precedes=extlconv->n_cs_precedes;
591+
worklconv.n_sep_by_space=extlconv->n_sep_by_space;
592+
worklconv.p_sign_posn=extlconv->p_sign_posn;
593+
worklconv.n_sign_posn=extlconv->n_sign_posn;
543594

544595
/* Try to restore internal settings */
545596
if (save_lc_monetary)
546597
{
547598
if (!setlocale(LC_MONETARY,save_lc_monetary))
548-
elog(WARNING,"failed to restore old locale");
549-
pfree(save_lc_monetary);
599+
trouble= true;
550600
}
551601

552602
if (save_lc_numeric)
553603
{
554604
if (!setlocale(LC_NUMERIC,save_lc_numeric))
555-
elog(WARNING,"failed to restore old locale");
556-
pfree(save_lc_numeric);
605+
trouble= true;
557606
}
558607

559608
#ifdefWIN32
560609
/* Try to restore internal ctype settings */
561610
if (save_lc_ctype)
562611
{
563612
if (!setlocale(LC_CTYPE,save_lc_ctype))
564-
elog(WARNING,"failed to restore old locale");
565-
pfree(save_lc_ctype);
613+
trouble= true;
566614
}
567615
#endif
568616

617+
/*
618+
* At this point we've done our best to clean up, and can call functions
619+
* that might possibly throw errors with a clean conscience. But let's
620+
* make sure we don't leak any already-strdup'd fields in worklconv.
621+
*/
622+
PG_TRY();
623+
{
624+
intencoding;
625+
626+
/*
627+
* Report it if we failed to restore anything. Perhaps this should be
628+
* FATAL, rather than continuing with bad locale settings?
629+
*/
630+
if (trouble)
631+
elog(WARNING,"failed to restore old locale");
632+
633+
/* Release the pstrdup'd locale names */
634+
if (save_lc_monetary)
635+
pfree(save_lc_monetary);
636+
if (save_lc_numeric)
637+
pfree(save_lc_numeric);
638+
#ifdefWIN32
639+
if (save_lc_ctype)
640+
pfree(save_lc_ctype);
641+
#endif
642+
643+
/* If any of the preceding strdup calls failed, complain now. */
644+
if (!struct_lconv_is_valid(&worklconv))
645+
ereport(ERROR,
646+
(errcode(ERRCODE_OUT_OF_MEMORY),
647+
errmsg("out of memory")));
648+
649+
/*
650+
* Now we must perform encoding conversion from whatever's associated
651+
* with the locale into the database encoding.
652+
*/
653+
encoding=pg_get_encoding_from_locale(locale_numeric, true);
654+
655+
db_encoding_convert(encoding,&worklconv.decimal_point);
656+
db_encoding_convert(encoding,&worklconv.thousands_sep);
657+
/* grouping is not text and does not require conversion */
658+
659+
encoding=pg_get_encoding_from_locale(locale_monetary, true);
660+
661+
db_encoding_convert(encoding,&worklconv.int_curr_symbol);
662+
db_encoding_convert(encoding,&worklconv.currency_symbol);
663+
db_encoding_convert(encoding,&worklconv.mon_decimal_point);
664+
db_encoding_convert(encoding,&worklconv.mon_thousands_sep);
665+
/* mon_grouping is not text and does not require conversion */
666+
db_encoding_convert(encoding,&worklconv.positive_sign);
667+
db_encoding_convert(encoding,&worklconv.negative_sign);
668+
}
669+
PG_CATCH();
670+
{
671+
free_struct_lconv(&worklconv);
672+
PG_RE_THROW();
673+
}
674+
PG_END_TRY();
675+
676+
/*
677+
* Everything is good, so save the results.
678+
*/
679+
CurrentLocaleConv=worklconv;
680+
CurrentLocaleConvAllocated= true;
569681
CurrentLocaleConvValid= true;
570682
return&CurrentLocaleConv;
571683
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp