From a45a630539edb1d8554608e76a7a03160ecbd3a8 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 8 Jan 2022 15:47:33 -0800 Subject: [PATCH] Fix unintentional locale dependency (#3358) Avoid unintentional locale dependency hb_variation_to_string uses sprintf with %g, which will produce a locale-dependent decimal point, which is not desired here. The output is supposed to be compatible with CSS syntax, and that always uses '.' for the decimal point. Fix this by changing the per-thread locale to "C" around sprintf call. Fixes https://github.com/harfbuzz/harfbuzz/issues/3355 Fixes https://github.com/harfbuzz/harfbuzz/pull/3357 Fixes https://github.com/harfbuzz/harfbuzz/pull/3358 Co-authored-by: Matthias Clasen --- configure.ac | 4 +-- meson.build | 3 +++ src/hb-common.cc | 64 +++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index 605cfbe7d..5414ea12f 100644 --- a/configure.ac +++ b/configure.ac @@ -68,8 +68,8 @@ GTK_DOC_CHECK([1.15],[--flavour no-tmpl]) ]) # Functions and headers -AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty) -AC_CHECK_HEADERS(unistd.h sys/mman.h stdbool.h) +AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale uselocale) +AC_CHECK_HEADERS(unistd.h sys/mman.h stdbool.h xlocale.h) # Compiler flags AC_CANONICAL_HOST diff --git a/meson.build b/meson.build index 5dd936ced..c88924006 100644 --- a/meson.build +++ b/meson.build @@ -61,6 +61,7 @@ check_headers = [ ['unistd.h'], ['sys/mman.h'], ['stdbool.h'], + ['xlocale.h'], ] check_funcs = [ @@ -70,6 +71,8 @@ check_funcs = [ ['getpagesize'], ['mmap'], ['isatty'], + ['uselocale'], + ['newlocale'], ] m_dep = cpp.find_library('m', required: false) diff --git a/src/hb-common.cc b/src/hb-common.cc index 26c8ad0f4..ea8ca2a24 100644 --- a/src/hb-common.cc +++ b/src/hb-common.cc @@ -31,6 +31,10 @@ #include +#ifdef HAVE_XLOCALE_H +#include // Needed on BSD/OS X for uselocale +#endif + #ifdef HB_NO_SETLOCALE #define setlocale(Category, Locale) "C" #endif @@ -122,7 +126,7 @@ hb_tag_from_string (const char *str, int len) * @tag: #hb_tag_t to convert * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string * - * Converts an #hb_tag_t to a string and returns it in @buf. + * Converts an #hb_tag_t to a string and returns it in @buf. * Strings will be four characters long. * * Since: 0.9.5 @@ -151,13 +155,13 @@ const char direction_strings[][4] = { * @str: (array length=len) (element-type uint8_t): String to convert * @len: Length of @str, or -1 if it is %NULL-terminated * - * Converts a string to an #hb_direction_t. + * Converts a string to an #hb_direction_t. * * Matching is loose and applies only to the first letter. For * examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR. * * Unmatched strings will return #HB_DIRECTION_INVALID. - * + * * Return value: The #hb_direction_t matching @str * * Since: 0.9.2 @@ -1039,6 +1043,56 @@ hb_variation_from_string (const char *str, int len, return false; } +#if !defined(HARFBUZZ_NO_SETLOCALE) && defined(HAVE_NEWLOCALE) && defined(HAVE_USELOCALE) + +#ifdef WIN32 +using locale_t = _locale_t; +#endif + +static inline void free_static_C_locale (); + +static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t, + hb_C_locale_lazy_loader_t> +{ + static locale_t create () + { + locale_t l = newlocale (LC_ALL_MASK, "C", NULL); + if (!l) + return l; + + hb_atexit (free_static_C_locale); + + return l; + } + static void destroy (locale_t l) + { + freelocale (l); + } + static locale_t get_null () + { + return (locale_t) 0; + } +} static_C_locale; + +static inline +void free_static_C_locale () +{ + static_C_locale.free_instance (); +} + +static locale_t +get_C_locale () +{ + return static_C_locale.get_unconst (); +} + +#else +#ifdef WIN32 +#define locale_t void * +#endif +#define uselocale(Locale) ((locale_t) 0) +#endif + /** * hb_variation_to_string: * @variation: an #hb_variation_t to convert @@ -1064,7 +1118,11 @@ hb_variation_to_string (hb_variation_t *variation, while (len && s[len - 1] == ' ') len--; s[len++] = '='; + + locale_t oldlocale HB_UNUSED; + oldlocale = uselocale (get_C_locale ()); len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value)); + (void) uselocale (oldlocale); assert (len < ARRAY_LENGTH (s)); len = hb_min (len, size - 1);