Skip to content

Commit bc37e90

Browse files
committed
Perl_sv_vcatpvfn_flags: set locale at most once
Calls to external snprintf-ish functions or that directly access PL_numeric_radix_sv are supposed to sandwich this access within STORE_LC_NUMERIC_SET_TO_NEEDED(); .... RESTORE_LC_NUMERIC(); The code in Perl_sv_vcatpvfn_flags() seems to have gotten a bit confused as to whether its trying to only set STORE_LC_NUMERIC_SET_TO_NEEDED() once, then handle one of more %[aefh] format elements, then only restore on exit. There is code at the end of the function which says: RESTORE_LC_NUMERIC(); /* Done outside loop, so don't have to save/restore each iteration. */ but in practice various places within this function (and its helper function S_format_hexfp() inconsistently repeatedly do STORE_LC_NUMERIC_SET_TO_NEEDED(); and sometime do RESTORE_LC_NUMERIC(). This commit changes it so that STORE_LC_NUMERIC_SET_TO_NEEDED() is called at most once, the first time a % format involving a radix point is encountered, and does RESTORE_LC_NUMERIC(); exactly once at the end of the function. Note that while calling STORE_LC_NUMERIC_SET_TO_NEEDED() multiple times is harmless, its quite expensive, as each time it has to check whether it's in the scope of 'use locale'. RESTORE_LC_NUMERIC() is cheap if STORE_LC_NUMERIC_SET_TO_NEEDED() earlier determined that there was nothing to do.
1 parent 9cc0d83 commit bc37e90

File tree

1 file changed

+14
-9
lines changed

1 file changed

+14
-9
lines changed

sv.c

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11449,6 +11449,8 @@ S_hextract(pTHX_ const NV nv, int* exponent, bool *subnormal,
1144911449
* string.
1145011450
* The rest of the args have the same meaning as the local vars of the
1145111451
* same name within Perl_sv_vcatpvfn_flags().
11452+
*
11453+
* It assumes the caller has already done STORE_LC_NUMERIC_SET_TO_NEEDED();
1145211454
*/
1145311455

1145411456
static STRLEN
@@ -11476,7 +11478,6 @@ S_format_hexfp(pTHX_ char * const buf, const STRLEN bufsize, const char c,
1147611478
bool subnormal = FALSE; /* IEEE 754 subnormal/denormal */
1147711479
bool negative = FALSE;
1147811480
STRLEN elen;
11479-
DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
1148011481

1148111482
/* XXX: NaN, Inf -- though they are printed as "NaN" and "Inf".
1148211483
*
@@ -11666,7 +11667,6 @@ S_format_hexfp(pTHX_ char * const buf, const STRLEN bufsize, const char c,
1166611667
#ifndef USE_LOCALE_NUMERIC
1166711668
*p++ = '.';
1166811669
#else
11669-
STORE_LC_NUMERIC_SET_TO_NEEDED();
1167011670
if (PL_numeric_radix_sv && IN_LC(LC_NUMERIC)) {
1167111671
STRLEN n;
1167211672
const char* r = SvPV(PL_numeric_radix_sv, n);
@@ -11676,7 +11676,6 @@ S_format_hexfp(pTHX_ char * const buf, const STRLEN bufsize, const char c,
1167611676
else {
1167711677
*p++ = '.';
1167811678
}
11679-
RESTORE_LC_NUMERIC();
1168011679
#endif
1168111680
}
1168211681

@@ -11790,8 +11789,10 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
1179011789
* Plus 32: Playing safe. */
1179111790
char ebuf[IV_DIG * 4 + NV_DIG + 32];
1179211791
bool no_redundant_warning = FALSE; /* did we use any explicit format parameter index? */
11793-
11792+
#ifdef USE_LOCALE_NUMERIC
1179411793
DECLARATION_FOR_LC_NUMERIC_MANIPULATION;
11794+
bool lc_numeric_set = FALSE; /* called STORE_LC_NUMERIC_SET_TO_NEEDED? */
11795+
#endif
1179511796

1179611797
PERL_ARGS_ASSERT_SV_VCATPVFN_FLAGS;
1179711798
PERL_UNUSED_ARG(maybe_tainted);
@@ -12750,14 +12751,21 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
1275012751
* below, or implicitly, via an snprintf() variant.
1275112752
* Note also things like ps_AF.utf8 which has
1275212753
* "\N{ARABIC DECIMAL SEPARATOR} as a radix point */
12753-
STORE_LC_NUMERIC_SET_TO_NEEDED();
12754+
if (!lc_numeric_set) {
12755+
/* only set once and reuse in-locale value on subsequent
12756+
* iterations.
12757+
* XXX what happens if we die in an eval?
12758+
*/
12759+
STORE_LC_NUMERIC_SET_TO_NEEDED();
12760+
lc_numeric_set = TRUE;
12761+
}
12762+
1275412763
if (PL_numeric_radix_sv && IN_LC(LC_NUMERIC)) {
1275512764
radix_len = SvCUR(PL_numeric_radix_sv);
1275612765
/* note that this will convert the output to utf8 even if
1275712766
* if the radix point didn't get output */
1275812767
is_utf8 = SvUTF8(PL_numeric_radix_sv);
1275912768
}
12760-
RESTORE_LC_NUMERIC();
1276112769
#endif
1276212770
/* this can't wrap unless PL_numeric_radix_sv is a string
1276312771
* consuming virtually all the 32-bit or 64-bit address space
@@ -12830,7 +12838,6 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
1283012838
&& fill != '0'
1283112839
&& intsize != 'q'
1283212840
) {
12833-
STORE_LC_NUMERIC_SET_TO_NEEDED();
1283412841
SNPRINTF_G(fv, ebuf, sizeof(ebuf), precis);
1283512842
elen = strlen(ebuf);
1283612843
eptr = ebuf;
@@ -12918,8 +12925,6 @@ Perl_sv_vcatpvfn_flags(pTHX_ SV *const sv, const char *const pat, const STRLEN p
1291812925
* where printf() taints but print($float) doesn't.
1291912926
* --jhi */
1292012927

12921-
STORE_LC_NUMERIC_SET_TO_NEEDED();
12922-
1292312928
/* hopefully the above makes ptr a very constrained format
1292412929
* that is safe to use, even though it's not literal */
1292512930
GCC_DIAG_IGNORE(-Wformat-nonliteral);

0 commit comments

Comments
 (0)