summaryrefslogtreecommitdiffstats
path: root/source/x/fontconfig/fedora-patches/fontconfig-mt.patch
diff options
context:
space:
mode:
Diffstat (limited to 'source/x/fontconfig/fedora-patches/fontconfig-mt.patch')
-rw-r--r--source/x/fontconfig/fedora-patches/fontconfig-mt.patch1439
1 files changed, 1439 insertions, 0 deletions
diff --git a/source/x/fontconfig/fedora-patches/fontconfig-mt.patch b/source/x/fontconfig/fedora-patches/fontconfig-mt.patch
new file mode 100644
index 000000000..31a3748c8
--- /dev/null
+++ b/source/x/fontconfig/fedora-patches/fontconfig-mt.patch
@@ -0,0 +1,1439 @@
+From b5bcf61fe789e66df2de609ec246cb7e4d326180 Mon Sep 17 00:00:00 2001
+From: Akira TAGOH <akira@tagoh.org>
+Date: Fri, 1 Nov 2019 14:43:42 +0900
+Subject: [PATCH 3/9] Use FcConfigReference/Destroy appropriately instead of
+ FcConfigGetCurrent
+
+This may improves to be MT-safe.
+
+Reported at https://bugs.chromium.org/p/chromium/issues/detail?id=1004254
+---
+ doc/fcconfig.fncs | 20 ++++
+ fontconfig/fontconfig.h | 4 +-
+ src/fccache.c | 73 +++++++++---
+ src/fccfg.c | 237 ++++++++++++++++++++++++---------------
+ src/fcdir.c | 31 ++++-
+ src/fcinit.c | 15 ++-
+ src/fclist.c | 25 +++--
+ src/fcmatch.c | 48 ++++----
+ test/Makefile.am | 5 +
+ test/run-test.sh | 15 +++
+ test/test-crbug1004254.c | 116 +++++++++++++++++++
+ 11 files changed, 436 insertions(+), 153 deletions(-)
+ create mode 100644 test/test-crbug1004254.c
+
+diff --git a/doc/fcconfig.fncs b/doc/fcconfig.fncs
+index 82769d5..e709b54 100644
+--- a/doc/fcconfig.fncs
++++ b/doc/fcconfig.fncs
+@@ -174,6 +174,10 @@ Returns one of the two sets of fonts from the configuration as specified
+ by <parameter>set</parameter>. This font set is owned by the library and must
+ not be modified or freed.
+ If <parameter>config</parameter> is NULL, the current configuration is used.
++ </para><para>
++This function isn't MT-safe. <function>FcConfigReference</function> must be called
++before using this and then <function>FcConfigDestroy</function> when
++the return value is no longer referenced.
+ @@
+
+ @RET@ FcBlanks *
+@@ -407,6 +411,10 @@ parse error, semantic error or allocation failure. Otherwise returns FcTrue.
+ Obtains the system root directory in 'config' if available. All files
+ (including file properties in patterns) obtained from this 'config' are
+ relative to this system root directory.
++ </para><para>
++This function isn't MT-safe. <function>FcConfigReference</function> must be called
++before using this and then <function>FcConfigDestroy</function> when
++the return value is no longer referenced.
+ @SINCE@ 2.10.92
+ @@
+
+@@ -433,6 +441,10 @@ When setting this on the current config this causes changing current config
+ @PURPOSE@ Initialize the iterator
+ @DESC@
+ Initialize 'iter' with the first iterator in the config file information list.
++ </para><para>
++This function isn't MT-safe. <function>FcConfigReference</function> must be called
++before using this and then <function>FcConfigDestroy</function> when the relevant
++values are no longer referenced.
+ @SINCE@ 2.12.91
+ @@
+
+@@ -444,6 +456,10 @@ Initialize 'iter' with the first iterator in the config file information list.
+ @DESC@
+ Set 'iter' to point to the next node in the config file information list.
+ If there is no next node, FcFalse is returned.
++ </para><para>
++This function isn't MT-safe. <function>FcConfigReference</function> must be called
++before using <function>FcConfigFileInfoIterInit</function> and then
++<function>FcConfigDestroy</function> when the relevant values are no longer referenced.
+ @SINCE@ 2.12.91
+ @@
+
+@@ -459,5 +475,9 @@ If there is no next node, FcFalse is returned.
+ Obtain the filename, the description and the flag whether it is enabled or not
+ for 'iter' where points to current configuration file information.
+ If the iterator is invalid, FcFalse is returned.
++ </para><para>
++This function isn't MT-safe. <function>FcConfigReference</function> must be called
++before using <function>FcConfigFileInfoIterInit</function> and then
++<function>FcConfigDestroy</function> when the relevant values are no longer referenced.
+ @SINCE@ 2.12.91
+ @@
+diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h
+index 2f0e8cf..c795245 100644
+--- a/fontconfig/fontconfig.h
++++ b/fontconfig/fontconfig.h
+@@ -375,7 +375,7 @@ FcPublic FcBool
+ FcDirCacheClean (const FcChar8 *cache_dir, FcBool verbose);
+
+ FcPublic void
+-FcCacheCreateTagFile (const FcConfig *config);
++FcCacheCreateTagFile (FcConfig *config);
+
+ FcPublic FcBool
+ FcDirCacheCreateUUID (FcChar8 *dir,
+@@ -437,7 +437,7 @@ FcPublic FcBlanks *
+ FcConfigGetBlanks (FcConfig *config);
+
+ FcPublic FcStrList *
+-FcConfigGetCacheDirs (const FcConfig *config);
++FcConfigGetCacheDirs (FcConfig *config);
+
+ FcPublic int
+ FcConfigGetRescanInterval (FcConfig *config);
+diff --git a/src/fccache.c b/src/fccache.c
+index c565560..d8f1dab 100644
+--- a/src/fccache.c
++++ b/src/fccache.c
+@@ -58,11 +58,15 @@ FcDirCacheDeleteUUID (const FcChar8 *dir,
+ {
+ FcBool ret = FcTrue;
+ #ifndef _WIN32
+- const FcChar8 *sysroot = FcConfigGetSysRoot (config);
++ const FcChar8 *sysroot;
+ FcChar8 *target, *d;
+ struct stat statb;
+ struct timeval times[2];
+
++ config = FcConfigReference (config);
++ if (!config)
++ return FcFalse;
++ sysroot = FcConfigGetSysRoot (config);
+ if (sysroot)
+ d = FcStrBuildFilename (sysroot, dir, NULL);
+ else
+@@ -94,6 +98,7 @@ FcDirCacheDeleteUUID (const FcChar8 *dir,
+ bail:
+ FcStrFree (d);
+ #endif
++ FcConfigDestroy (config);
+
+ return ret;
+ }
+@@ -265,7 +270,13 @@ FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
+ #endif
+ FcStrList *list;
+ FcChar8 *cache_dir;
+- const FcChar8 *sysroot = FcConfigGetSysRoot (config);
++ const FcChar8 *sysroot;
++ FcBool ret = FcTrue;
++
++ config = FcConfigReference (config);
++ if (!config)
++ return FcFalse;
++ sysroot = FcConfigGetSysRoot (config);
+
+ FcDirCacheBasenameMD5 (config, dir, cache_base);
+ #ifndef _WIN32
+@@ -274,7 +285,10 @@ FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
+
+ list = FcStrListCreate (config->cacheDirs);
+ if (!list)
+- return FcFalse;
++ {
++ ret = FcFalse;
++ goto bail;
++ }
+
+ while ((cache_dir = FcStrListNext (list)))
+ {
+@@ -304,8 +318,11 @@ FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
+ FcDirCacheDeleteUUID (dir, config);
+ /* return FcFalse if something went wrong */
+ if (cache_dir)
+- return FcFalse;
+- return FcTrue;
++ ret = FcFalse;
++bail:
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ static int
+@@ -1041,10 +1058,15 @@ FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file)
+ {
+ FcCache *cache = NULL;
+
++ config = FcConfigReference (config);
++ if (!config)
++ return NULL;
+ if (!FcDirCacheProcess (config, dir,
+ FcDirCacheMapHelper,
+ &cache, cache_file))
+- return NULL;
++ cache = NULL;
++
++ FcConfigDestroy (config);
+
+ return cache;
+ }
+@@ -1055,13 +1077,16 @@ FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat)
+ int fd;
+ FcCache *cache;
+ struct stat my_file_stat;
++ FcConfig *config;
+
+ if (!file_stat)
+ file_stat = &my_file_stat;
+ fd = FcDirCacheOpenFile (cache_file, file_stat);
+ if (fd < 0)
+ return NULL;
+- cache = FcDirCacheMapFd (FcConfigGetCurrent (), fd, file_stat, NULL);
++ config = FcConfigReference (NULL);
++ cache = FcDirCacheMapFd (config, fd, file_stat, NULL);
++ FcConfigDestroy (config);
+ close (fd);
+ return cache;
+ }
+@@ -1155,12 +1180,16 @@ FcBool
+ FcDirCacheValid (const FcChar8 *dir)
+ {
+ FcConfig *config;
++ FcBool ret;
+
+- config = FcConfigGetCurrent ();
++ config = FcConfigReference (NULL);
+ if (!config)
+ return FcFalse;
+
+- return FcDirCacheValidConfig (dir, config);
++ ret = FcDirCacheValidConfig (dir, config);
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ /*
+@@ -1438,9 +1467,13 @@ FcDirCacheClean (const FcChar8 *cache_dir, FcBool verbose)
+ FcCache *cache;
+ struct stat target_stat;
+ const FcChar8 *sysroot;
++ FcConfig *config;
+
++ config = FcConfigReference (NULL);
++ if (!config)
++ return FcFalse;
+ /* FIXME: this API needs to support non-current FcConfig */
+- sysroot = FcConfigGetSysRoot (NULL);
++ sysroot = FcConfigGetSysRoot (config);
+ if (sysroot)
+ dir = FcStrBuildFilename (sysroot, cache_dir, NULL);
+ else
+@@ -1448,7 +1481,8 @@ FcDirCacheClean (const FcChar8 *cache_dir, FcBool verbose)
+ if (!dir)
+ {
+ fprintf (stderr, "Fontconfig error: %s: out of memory\n", cache_dir);
+- return FcFalse;
++ ret = FcFalse;
++ goto bail;
+ }
+ if (access ((char *) dir, W_OK) != 0)
+ {
+@@ -1525,8 +1559,10 @@ FcDirCacheClean (const FcChar8 *cache_dir, FcBool verbose)
+ }
+
+ closedir (d);
+- bail0:
++bail0:
+ FcStrFree (dir);
++bail:
++ FcConfigDestroy (config);
+
+ return ret;
+ }
+@@ -1968,15 +2004,20 @@ FcDirCacheCreateTagFile (const FcChar8 *cache_dir)
+ }
+
+ void
+-FcCacheCreateTagFile (const FcConfig *config)
++FcCacheCreateTagFile (FcConfig *config)
+ {
+ FcChar8 *cache_dir = NULL, *d = NULL;
+ FcStrList *list;
+- const FcChar8 *sysroot = FcConfigGetSysRoot (config);
++ const FcChar8 *sysroot;
++
++ config = FcConfigReference (config);
++ if (!config)
++ return;
++ sysroot = FcConfigGetSysRoot (config);
+
+ list = FcConfigGetCacheDirs (config);
+ if (!list)
+- return;
++ goto bail;
+
+ while ((cache_dir = FcStrListNext (list)))
+ {
+@@ -1992,6 +2033,8 @@ FcCacheCreateTagFile (const FcConfig *config)
+ if (d)
+ FcStrFree (d);
+ FcStrListDone (list);
++bail:
++ FcConfigDestroy (config);
+ }
+
+ #define __fccache__
+diff --git a/src/fccfg.c b/src/fccfg.c
+index 21ccd25..11dc876 100644
+--- a/src/fccfg.c
++++ b/src/fccfg.c
+@@ -237,12 +237,12 @@ FcConfigUptoDate (FcConfig *config)
+ {
+ FcFileTime config_time, config_dir_time, font_time;
+ time_t now = time(0);
++ FcBool ret = FcTrue;
++
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return FcFalse;
+- }
++ return FcFalse;
++
+ config_time = FcConfigNewestFile (config->configFiles);
+ config_dir_time = FcConfigNewestFile (config->configDirs);
+ font_time = FcConfigNewestFile (config->fontDirs);
+@@ -258,13 +258,19 @@ FcConfigUptoDate (FcConfig *config)
+ fprintf (stderr,
+ "Fontconfig warning: Directory/file mtime in the future. New fonts may not be detected.\n");
+ config->rescanTime = now;
+- return FcTrue;
++ goto bail;
+ }
+ else
+- return FcFalse;
++ {
++ ret = FcFalse;
++ goto bail;
++ }
+ }
+ config->rescanTime = now;
+- return FcTrue;
++bail:
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ FcExpr *
+@@ -291,11 +297,26 @@ FcConfigReference (FcConfig *config)
+ {
+ if (!config)
+ {
+- config = FcConfigGetCurrent ();
++ /* Do not use FcConfigGetCurrent () for the purpose of obtaining current FcConfig here.
++ * because the reference counter must be increased before setting it to _fcConfig.
++ */
++ retry:
++ config = fc_atomic_ptr_get (&_fcConfig);
+ if (!config)
+- return 0;
+- }
++ {
++ config = FcConfigCreate ();
++ FcRefInc (&config->ref);
+
++ config = FcInitLoadOwnConfigAndFonts (config);
++ if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config))
++ {
++ FcConfigDestroy (config); /* To decrease the refcount for the above one. */
++ FcConfigDestroy (config); /* To destroy it actualy */
++ goto retry;
++ }
++ return config;
++ }
++ }
+ FcRefInc (&config->ref);
+
+ return config;
+@@ -475,25 +496,32 @@ FcBool
+ FcConfigBuildFonts (FcConfig *config)
+ {
+ FcFontSet *fonts;
++ FcBool ret = FcTrue;
+
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return FcFalse;
+- }
++ return FcFalse;
+
+ fonts = FcFontSetCreate ();
+ if (!fonts)
+- return FcFalse;
++ {
++ ret = FcFalse;
++ goto bail;
++ }
+
+ FcConfigSetFonts (config, fonts, FcSetSystem);
+
+ if (!FcConfigAddDirList (config, FcSetSystem, config->fontDirs))
+- return FcFalse;
++ {
++ ret = FcFalse;
++ goto bail;
++ }
+ if (FcDebug () & FC_DBG_FONTSET)
+ FcFontSetPrint (fonts);
+- return FcTrue;
++bail:
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ FcBool
+@@ -537,13 +565,15 @@ FcConfigAddConfigDir (FcConfig *config,
+ FcStrList *
+ FcConfigGetConfigDirs (FcConfig *config)
+ {
++ FcStrList *ret;
++
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return 0;
+- }
+- return FcStrListCreate (config->configDirs);
++ return NULL;
++ ret = FcStrListCreate (config->configDirs);
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ FcBool
+@@ -579,13 +609,15 @@ FcConfigResetFontDirs (FcConfig *config)
+ FcStrList *
+ FcConfigGetFontDirs (FcConfig *config)
+ {
++ FcStrList *ret;
++
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return 0;
+- }
+- return FcStrListCreate (config->fontDirs);
++ return NULL;
++ ret = FcStrListCreate (config->fontDirs);
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ static FcBool
+@@ -670,15 +702,17 @@ FcConfigAddCacheDir (FcConfig *config,
+ }
+
+ FcStrList *
+-FcConfigGetCacheDirs (const FcConfig *config)
++FcConfigGetCacheDirs (FcConfig *config)
+ {
++ FcStrList *ret;
++
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return 0;
+- }
+- return FcStrListCreate (config->cacheDirs);
++ return NULL;
++ ret = FcStrListCreate (config->cacheDirs);
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ FcBool
+@@ -699,13 +733,15 @@ FcConfigAddConfigFile (FcConfig *config,
+ FcStrList *
+ FcConfigGetConfigFiles (FcConfig *config)
+ {
++ FcStrList *ret;
++
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return 0;
+- }
+- return FcStrListCreate (config->configFiles);
++ return NULL;
++ ret = FcStrListCreate (config->configFiles);
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ FcChar8 *
+@@ -784,25 +820,26 @@ FcConfigAddBlank (FcConfig *config FC_UNUSED,
+ int
+ FcConfigGetRescanInterval (FcConfig *config)
+ {
++ int ret;
++
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return 0;
+- }
+- return config->rescanInterval;
++ return 0;
++ ret = config->rescanInterval;
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ FcBool
+ FcConfigSetRescanInterval (FcConfig *config, int rescanInterval)
+ {
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return FcFalse;
+- }
++ return FcFalse;
+ config->rescanInterval = rescanInterval;
++ FcConfigDestroy (config);
++
+ return FcTrue;
+ }
+
+@@ -1670,15 +1707,13 @@ FcConfigSubstituteWithPat (FcConfig *config,
+ FcBool retval = FcTrue;
+ FcTest **tst = NULL;
+
+- if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return FcFalse;
+- }
+-
+ if (kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
+ return FcFalse;
++
++ config = FcConfigReference (config);
++ if (!config)
++ return FcFalse;
++
+ s = config->subst[kind];
+ if (kind == FcMatchPattern)
+ {
+@@ -1973,6 +2008,7 @@ bail1:
+ free (value);
+ if (tst)
+ free (tst);
++ FcConfigDestroy (config);
+
+ return retval;
+ }
+@@ -2290,12 +2326,9 @@ FcConfigGetFilename (FcConfig *config,
+ FcChar8 *file, *dir, **path, **p;
+ const FcChar8 *sysroot;
+
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return NULL;
+- }
++ return NULL;
+ sysroot = FcConfigGetSysRoot (config);
+ if (!url || !*url)
+ {
+@@ -2306,7 +2339,10 @@ FcConfigGetFilename (FcConfig *config,
+ file = 0;
+
+ if (FcStrIsAbsoluteFilename(url))
+- return FcConfigFileExists (sysroot, url);
++ {
++ file = FcConfigFileExists (sysroot, url);
++ goto bail;
++ }
+
+ if (*url == '~')
+ {
+@@ -2330,7 +2366,10 @@ FcConfigGetFilename (FcConfig *config,
+ {
+ path = FcConfigGetPath ();
+ if (!path)
+- return NULL;
++ {
++ file = NULL;
++ goto bail;
++ }
+ for (p = path; *p; p++)
+ {
+ FcChar8 *s;
+@@ -2347,6 +2386,9 @@ FcConfigGetFilename (FcConfig *config,
+ }
+ FcConfigFreePath (path);
+ }
++bail:
++ FcConfigDestroy (config);
++
+ return file;
+ }
+
+@@ -2409,17 +2451,18 @@ FcConfigAppFontAddFile (FcConfig *config,
+ FcStrSet *subdirs;
+ FcStrList *sublist;
+ FcChar8 *subdir;
++ FcBool ret = FcTrue;
+
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return FcFalse;
+- }
++ return FcFalse;
+
+ subdirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
+ if (!subdirs)
+- return FcFalse;
++ {
++ ret = FcFalse;
++ goto bail;
++ }
+
+ set = FcConfigGetFonts (config, FcSetApplication);
+ if (!set)
+@@ -2428,7 +2471,8 @@ FcConfigAppFontAddFile (FcConfig *config,
+ if (!set)
+ {
+ FcStrSetDestroy (subdirs);
+- return FcFalse;
++ ret = FcFalse;
++ goto bail;
+ }
+ FcConfigSetFonts (config, set, FcSetApplication);
+ }
+@@ -2436,7 +2480,8 @@ FcConfigAppFontAddFile (FcConfig *config,
+ if (!FcFileScanConfig (set, subdirs, file, config))
+ {
+ FcStrSetDestroy (subdirs);
+- return FcFalse;
++ ret = FcFalse;
++ goto bail;
+ }
+ if ((sublist = FcStrListCreate (subdirs)))
+ {
+@@ -2447,7 +2492,10 @@ FcConfigAppFontAddFile (FcConfig *config,
+ FcStrListDone (sublist);
+ }
+ FcStrSetDestroy (subdirs);
+- return FcTrue;
++bail:
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ FcBool
+@@ -2456,17 +2504,18 @@ FcConfigAppFontAddDir (FcConfig *config,
+ {
+ FcFontSet *set;
+ FcStrSet *dirs;
++ FcBool ret = FcTrue;
+
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return FcFalse;
+- }
++ return FcFalse;
+
+ dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
+ if (!dirs)
+- return FcFalse;
++ {
++ ret = FcFalse;
++ goto bail;
++ }
+
+ set = FcConfigGetFonts (config, FcSetApplication);
+ if (!set)
+@@ -2475,7 +2524,8 @@ FcConfigAppFontAddDir (FcConfig *config,
+ if (!set)
+ {
+ FcStrSetDestroy (dirs);
+- return FcFalse;
++ ret = FcFalse;
++ goto bail;
+ }
+ FcConfigSetFonts (config, set, FcSetApplication);
+ }
+@@ -2485,23 +2535,26 @@ FcConfigAppFontAddDir (FcConfig *config,
+ if (!FcConfigAddDirList (config, FcSetApplication, dirs))
+ {
+ FcStrSetDestroy (dirs);
+- return FcFalse;
++ ret = FcFalse;
++ goto bail;
+ }
+ FcStrSetDestroy (dirs);
+- return FcTrue;
++bail:
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ void
+ FcConfigAppFontClear (FcConfig *config)
+ {
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return;
+- }
++ return;
+
+ FcConfigSetFonts (config, 0, FcSetApplication);
++
++ FcConfigDestroy (config);
+ }
+
+ /*
+diff --git a/src/fcdir.c b/src/fcdir.c
+index 7d7b23c..693c845 100644
+--- a/src/fcdir.c
++++ b/src/fcdir.c
+@@ -167,7 +167,16 @@ FcFileScan (FcFontSet *set,
+ const FcChar8 *file,
+ FcBool force FC_UNUSED)
+ {
+- return FcFileScanConfig (set, dirs, file, FcConfigGetCurrent ());
++ FcConfig *config;
++ FcBool ret;
++
++ config = FcConfigReference (NULL);
++ if (!config)
++ return FcFalse;
++ ret = FcFileScanConfig (set, dirs, file, config);
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ /*
+@@ -271,10 +280,19 @@ FcDirScan (FcFontSet *set,
+ const FcChar8 *dir,
+ FcBool force FC_UNUSED)
+ {
++ FcConfig *config;
++ FcBool ret;
++
+ if (cache || !force)
+ return FcFalse;
+
+- return FcDirScanConfig (set, dirs, dir, force, FcConfigGetCurrent ());
++ config = FcConfigReference (NULL);
++ if (!config)
++ return FcFalse;
++ ret = FcDirScanConfig (set, dirs, dir, force, config);
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ /*
+@@ -353,12 +371,16 @@ FcDirCacheRescan (const FcChar8 *dir, FcConfig *config)
+ FcCache *new = NULL;
+ struct stat dir_stat;
+ FcStrSet *dirs;
+- const FcChar8 *sysroot = FcConfigGetSysRoot (config);
++ const FcChar8 *sysroot;
+ FcChar8 *d = NULL;
+ #ifndef _WIN32
+ int fd = -1;
+ #endif
+
++ config = FcConfigReference (config);
++ if (!config)
++ return NULL;
++ sysroot = FcConfigGetSysRoot (config);
+ cache = FcDirCacheLoad (dir, config, NULL);
+ if (!cache)
+ goto bail;
+@@ -401,6 +423,7 @@ bail1:
+ bail:
+ if (d)
+ FcStrFree (d);
++ FcConfigDestroy (config);
+
+ return new;
+ }
+@@ -413,6 +436,7 @@ FcDirCacheRead (const FcChar8 *dir, FcBool force, FcConfig *config)
+ {
+ FcCache *cache = NULL;
+
++ config = FcConfigReference (config);
+ /* Try to use existing cache file */
+ if (!force)
+ cache = FcDirCacheLoad (dir, config, NULL);
+@@ -420,6 +444,7 @@ FcDirCacheRead (const FcChar8 *dir, FcBool force, FcConfig *config)
+ /* Not using existing cache file, construct new cache */
+ if (!cache)
+ cache = FcDirCacheScan (dir, config);
++ FcConfigDestroy (config);
+
+ return cache;
+ }
+diff --git a/src/fcinit.c b/src/fcinit.c
+index 5831a19..6f82ebd 100644
+--- a/src/fcinit.c
++++ b/src/fcinit.c
+@@ -229,7 +229,8 @@ FcInitReinitialize (void)
+ FcBool
+ FcInitBringUptoDate (void)
+ {
+- FcConfig *config = FcConfigGetCurrent ();
++ FcConfig *config = FcConfigReference (NULL);
++ FcBool ret = FcTrue;
+ time_t now;
+
+ if (!config)
+@@ -238,19 +239,23 @@ FcInitBringUptoDate (void)
+ * rescanInterval == 0 disables automatic up to date
+ */
+ if (config->rescanInterval == 0)
+- return FcTrue;
++ goto bail;
+ /*
+ * Check no more often than rescanInterval seconds
+ */
+ now = time (0);
+ if (config->rescanTime + config->rescanInterval - now > 0)
+- return FcTrue;
++ goto bail;
+ /*
+ * If up to date, don't reload configuration
+ */
+ if (FcConfigUptoDate (0))
+- return FcTrue;
+- return FcInitReinitialize ();
++ goto bail;
++ ret = FcInitReinitialize ();
++bail:
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ #define __fcinit__
+diff --git a/src/fclist.c b/src/fclist.c
+index 494bdea..053803b 100644
+--- a/src/fclist.c
++++ b/src/fclist.c
+@@ -491,11 +491,10 @@ FcFontSetList (FcConfig *config,
+ {
+ if (!FcInitBringUptoDate ())
+ goto bail0;
+-
+- config = FcConfigGetCurrent ();
+- if (!config)
+- goto bail0;
+ }
++ config = FcConfigReference (config);
++ if (!config)
++ goto bail0;
+ FcListHashTableInit (&table);
+
+ if (!os)
+@@ -558,7 +557,7 @@ FcFontSetList (FcConfig *config,
+ */
+ ret = FcFontSetCreate ();
+ if (!ret)
+- goto bail0;
++ goto bail1;
+ for (i = 0; i < FC_LIST_HASH_SIZE; i++)
+ while ((bucket = table.buckets[i]))
+ {
+@@ -570,6 +569,7 @@ FcFontSetList (FcConfig *config,
+
+ if (destroy_os)
+ FcObjectSetDestroy (os);
++ FcConfigDestroy (config);
+
+ return ret;
+
+@@ -577,6 +577,7 @@ bail2:
+ FcFontSetDestroy (ret);
+ bail1:
+ FcListHashTableCleanup (&table);
++ FcConfigDestroy (config);
+ bail0:
+ if (destroy_os)
+ FcObjectSetDestroy (os);
+@@ -588,24 +589,26 @@ FcFontList (FcConfig *config,
+ FcPattern *p,
+ FcObjectSet *os)
+ {
+- FcFontSet *sets[2];
++ FcFontSet *sets[2], *ret;
+ int nsets;
+
+ if (!config)
+ {
+ if (!FcInitBringUptoDate ())
+ return 0;
+-
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return 0;
+ }
++ config = FcConfigReference (config);
++ if (!config)
++ return NULL;
+ nsets = 0;
+ if (config->fonts[FcSetSystem])
+ sets[nsets++] = config->fonts[FcSetSystem];
+ if (config->fonts[FcSetApplication])
+ sets[nsets++] = config->fonts[FcSetApplication];
+- return FcFontSetList (config, sets, nsets, p, os);
++ ret = FcFontSetList (config, sets, nsets, p, os);
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+ #define __fclist__
+ #include "fcaliastail.h"
+diff --git a/src/fcmatch.c b/src/fcmatch.c
+index 78bcf7b..3bc352b 100644
+--- a/src/fcmatch.c
++++ b/src/fcmatch.c
+@@ -845,7 +845,7 @@ FcFontSetMatch (FcConfig *config,
+ FcPattern *p,
+ FcResult *result)
+ {
+- FcPattern *best;
++ FcPattern *best, *ret = NULL;
+
+ assert (sets != NULL);
+ assert (p != NULL);
+@@ -853,17 +853,16 @@ FcFontSetMatch (FcConfig *config,
+
+ *result = FcResultNoMatch;
+
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return 0;
+- }
++ return NULL;
+ best = FcFontSetMatchInternal (sets, nsets, p, result);
+ if (best)
+- return FcFontRenderPrepare (config, p, best);
+- else
+- return NULL;
++ ret = FcFontRenderPrepare (config, p, best);
++
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ FcPattern *
+@@ -873,19 +872,16 @@ FcFontMatch (FcConfig *config,
+ {
+ FcFontSet *sets[2];
+ int nsets;
+- FcPattern *best;
++ FcPattern *best, *ret = NULL;
+
+ assert (p != NULL);
+ assert (result != NULL);
+
+ *result = FcResultNoMatch;
+
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return 0;
+- }
++ return NULL;
+ nsets = 0;
+ if (config->fonts[FcSetSystem])
+ sets[nsets++] = config->fonts[FcSetSystem];
+@@ -894,9 +890,11 @@ FcFontMatch (FcConfig *config,
+
+ best = FcFontSetMatchInternal (sets, nsets, p, result);
+ if (best)
+- return FcFontRenderPrepare (config, p, best);
+- else
+- return NULL;
++ ret = FcFontRenderPrepare (config, p, best);
++
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+
+ typedef struct _FcSortNode {
+@@ -1183,7 +1181,7 @@ FcFontSort (FcConfig *config,
+ FcCharSet **csp,
+ FcResult *result)
+ {
+- FcFontSet *sets[2];
++ FcFontSet *sets[2], *ret;
+ int nsets;
+
+ assert (p != NULL);
+@@ -1191,18 +1189,18 @@ FcFontSort (FcConfig *config,
+
+ *result = FcResultNoMatch;
+
++ config = FcConfigReference (config);
+ if (!config)
+- {
+- config = FcConfigGetCurrent ();
+- if (!config)
+- return 0;
+- }
++ return NULL;
+ nsets = 0;
+ if (config->fonts[FcSetSystem])
+ sets[nsets++] = config->fonts[FcSetSystem];
+ if (config->fonts[FcSetApplication])
+ sets[nsets++] = config->fonts[FcSetApplication];
+- return FcFontSetSort (config, sets, nsets, p, trim, csp, result);
++ ret = FcFontSetSort (config, sets, nsets, p, trim, csp, result);
++ FcConfigDestroy (config);
++
++ return ret;
+ }
+ #define __fcmatch__
+ #include "fcaliastail.h"
+diff --git a/test/Makefile.am b/test/Makefile.am
+index e44aa0b..aae45cb 100644
+--- a/test/Makefile.am
++++ b/test/Makefile.am
+@@ -42,6 +42,11 @@ test_pthread_LDADD = $(top_builddir)/src/libfontconfig.la
+ # We don't enable this test by default because it will require config and fonts
+ # to meaningfully test anything, and we are not installed yet.
+ #TESTS += test-pthread
++
++check_PROGRAMS += test-crbug1004254
++test_crbug1004254_LDADD = $(top_builddir)/src/libfontconfig.la
++# Disabling this for the same reason as above but trying to run in run-test.sh.
++#TESTS += test-crbug1004254
+ endif
+ check_PROGRAMS += test-bz89617
+ test_bz89617_CFLAGS = \
+diff --git a/test/run-test.sh b/test/run-test.sh
+index 8ad09e3..e1ee6d0 100644
+--- a/test/run-test.sh
++++ b/test/run-test.sh
+@@ -20,6 +20,8 @@
+ # DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ # PERFORMANCE OF THIS SOFTWARE.
++set -e
++
+ case "$OSTYPE" in
+ msys ) MyPWD=`pwd -W` ;; # On Msys/MinGW, returns a MS Windows style path.
+ * ) MyPWD=`pwd` ;; # On any other platforms, returns a Unix style path.
+@@ -408,4 +410,17 @@ rm -rf $MYCACHEBASEDIR $MYCONFIG my-fonts.conf my-out my-out.expected
+
+ fi # if [ "x$EXEEXT" = "x" ]
+
++if [ -x $BUILDTESTDIR/test-crbug1004254 ]; then
++ dotest "MT-safe global config"
++ prep
++ curl -s -o $FONTDIR/noto.zip https://noto-website-2.storage.googleapis.com/pkgs/NotoSans-hinted.zip
++ (cd $FONTDIR; unzip noto.zip)
++ if [ -n ${SOURCE_DATE_EPOCH:-} ] && [ ${#SOURCE_DATE_EPOCH} -gt 0 ]; then
++ touch -m -t "`date -d \"@${SOURCE_DATE_EPOCH}\" +%y%m%d%H%M.%S`" $FONTDIR
++ fi
++ $BUILDTESTDIR/test-crbug1004254
++else
++ echo "No test-crbug1004254: skipped"
++fi
++
+ rm -rf $FONTDIR $CACHEFILE $CACHEDIR $BASEDIR $FONTCONFIG_FILE out
+diff --git a/test/test-crbug1004254.c b/test/test-crbug1004254.c
+new file mode 100644
+index 0000000..1cc6fc7
+--- /dev/null
++++ b/test/test-crbug1004254.c
+@@ -0,0 +1,116 @@
++/*
++ * fontconfig/test/test-pthread.c
++ *
++ * Copyright © 2000 Keith Packard
++ * Copyright © 2013 Raimund Steger
++ *
++ * Permission to use, copy, modify, distribute, and sell this software and its
++ * documentation for any purpose is hereby granted without fee, provided that
++ * the above copyright notice appear in all copies and that both that
++ * copyright notice and this permission notice appear in supporting
++ * documentation, and that the name of the author(s) not be used in
++ * advertising or publicity pertaining to distribution of the software without
++ * specific, written prior permission. The authors make no
++ * representations about the suitability of this software for any purpose. It
++ * is provided "as is" without express or implied warranty.
++ *
++ * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
++ * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
++ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
++ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
++ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
++ * PERFORMANCE OF THIS SOFTWARE.
++ */
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <pthread.h>
++#include <fontconfig/fontconfig.h>
++
++struct thr_arg_s
++{
++ int thr_num;
++};
++
++static void
++run_query (void)
++{
++ FcPattern *pat = FcPatternCreate (), *match;
++ FcResult result;
++
++ FcPatternAddString (pat, FC_FAMILY, "sans-serif");
++ FcPatternAddBool (pat, FC_SCALABLE, FcTrue);
++ FcConfigSubstitute (NULL, pat, FcMatchPattern);
++ FcDefaultSubstitute (pat);
++ match = FcFontMatch (NULL, pat, &result);
++ if (result != FcResultMatch || !match)
++ {
++ fprintf (stderr, "ERROR: No matches found\n");
++ }
++ if (match)
++ FcPatternDestroy (match);
++ FcPatternDestroy (pat);
++}
++
++static void
++run_reinit (void)
++{
++ if (!FcInitReinitialize ())
++ {
++ fprintf (stderr, "ERROR: Reinitializing failed\n");
++ }
++}
++
++#define NTEST 3000
++
++static void *
++run_test_in_thread (void *arg)
++{
++ struct thr_arg_s *thr_arg = (struct thr_arg_s *) arg;
++ int thread_num = thr_arg->thr_num;
++
++ fprintf (stderr, "Worker %d: started (round %d)\n", thread_num % 2, thread_num / 2);
++ if ((thread_num % 2) == 0)
++ {
++ run_query ();
++ }
++ else
++ {
++ run_reinit ();
++ }
++ fprintf (stderr, "Worker %d: done (round %d)\n", thread_num % 2, thread_num / 2);
++
++ return NULL;
++}
++
++int
++main (int argc, char **argv)
++{
++ pthread_t threads[NTEST];
++ struct thr_arg_s thr_arg[NTEST];
++ int i, j;
++
++ for (i = 0; i < NTEST; i++)
++ {
++ int result;
++
++ fprintf (stderr, "Thread %d (worker %d round %d): creating\n", i, i % 2, i / 2);
++ thr_arg[i].thr_num = i;
++ result = pthread_create (&threads[i], NULL, run_test_in_thread,
++ (void *) &thr_arg[i]);
++ if (result != 0)
++ {
++ fprintf (stderr, "Cannot create thread %d\n", i);
++ break;
++ }
++ }
++ for (j = 0; j < i; j++)
++ {
++ pthread_join(threads[j], NULL);
++ fprintf (stderr, "Joined thread %d\n", j);
++ }
++ FcFini ();
++
++ return 0;
++}
+--
+2.24.1
+
+From aa8c8cfa9fb2563482336249e3f56459099fcf6e Mon Sep 17 00:00:00 2001
+From: Akira TAGOH <akira@tagoh.org>
+Date: Sat, 2 Nov 2019 00:14:48 +0900
+Subject: [PATCH 4/9] Fix potential race condition in FcConfigSetCurrent and
+ FcConfigReference
+
+---
+ src/fccache.c | 2 +
+ src/fccfg.c | 105 ++++++++++++++++++++++++++++++++++++++++++--------
+ 2 files changed, 90 insertions(+), 17 deletions(-)
+
+diff --git a/src/fccache.c b/src/fccache.c
+index d8f1dab..4744a84 100644
+--- a/src/fccache.c
++++ b/src/fccache.c
+@@ -1085,6 +1085,8 @@ FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat)
+ if (fd < 0)
+ return NULL;
+ config = FcConfigReference (NULL);
++ if (!config)
++ return NULL;
+ cache = FcDirCacheMapFd (config, fd, file_stat, NULL);
+ FcConfigDestroy (config);
+ close (fd);
+diff --git a/src/fccfg.c b/src/fccfg.c
+index 11dc876..30f37af 100644
+--- a/src/fccfg.c
++++ b/src/fccfg.c
+@@ -33,6 +33,49 @@
+ #endif
+
+ static FcConfig *_fcConfig; /* MT-safe */
++static FcMutex *_lock;
++
++static void
++lock_config (void)
++{
++ FcMutex *lock;
++retry:
++ lock = fc_atomic_ptr_get (&_lock);
++ if (!lock)
++ {
++ lock = (FcMutex *) malloc (sizeof (FcMutex));
++ FcMutexInit (lock);
++ if (!fc_atomic_ptr_cmpexch (&_lock, NULL, lock))
++ {
++ FcMutexFinish (lock);
++ goto retry;
++ }
++ FcMutexLock (lock);
++ /* Initialize random state */
++ FcRandom ();
++ return;
++ }
++ FcMutexLock (lock);
++}
++
++static void
++unlock_config (void)
++{
++ FcMutexUnlock (_lock);
++}
++
++static void
++free_lock (void)
++{
++ FcMutex *lock;
++
++ lock = fc_atomic_ptr_get (&_lock);
++ if (lock && fc_atomic_ptr_cmpexch (&_lock, lock, NULL))
++ {
++ FcMutexFinish (lock);
++ free (lock);
++ }
++}
+
+ static FcConfig *
+ FcConfigEnsure (void)
+@@ -44,8 +87,9 @@ retry:
+ {
+ config = FcInitLoadConfigAndFonts ();
+
+- if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
+- FcConfigDestroy (config);
++ if (!config || !fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
++ if (config)
++ FcConfigDestroy (config);
+ goto retry;
+ }
+ }
+@@ -76,6 +120,7 @@ FcConfigFini (void)
+ FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig);
+ if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
+ FcConfigDestroy (cfg);
++ free_lock ();
+ }
+
+ static FcChar8 *
+@@ -297,27 +342,31 @@ FcConfigReference (FcConfig *config)
+ {
+ if (!config)
+ {
+- /* Do not use FcConfigGetCurrent () for the purpose of obtaining current FcConfig here.
+- * because the reference counter must be increased before setting it to _fcConfig.
++ /* lock during obtaining the value from _fcConfig and count up refcount there,
++ * there are the race between them.
+ */
++ lock_config ();
+ retry:
+ config = fc_atomic_ptr_get (&_fcConfig);
+ if (!config)
+ {
+- config = FcConfigCreate ();
+- FcRefInc (&config->ref);
++ unlock_config ();
+
+- config = FcInitLoadOwnConfigAndFonts (config);
++ config = FcInitLoadConfigAndFonts ();
++ if (!config)
++ goto retry;
++ lock_config ();
+ if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config))
+ {
+- FcConfigDestroy (config); /* To decrease the refcount for the above one. */
+- FcConfigDestroy (config); /* To destroy it actualy */
++ FcConfigDestroy (config);
+ goto retry;
+ }
+- return config;
+ }
++ FcRefInc (&config->ref);
++ unlock_config ();
+ }
+- FcRefInc (&config->ref);
++ else
++ FcRefInc (&config->ref);
+
+ return config;
+ }
+@@ -529,20 +578,29 @@ FcConfigSetCurrent (FcConfig *config)
+ {
+ FcConfig *cfg;
+
++ if (config)
++ {
++ if (!config->fonts[FcSetSystem])
++ if (!FcConfigBuildFonts (config))
++ return FcFalse;
++ FcRefInc (&config->ref);
++ }
++
++ lock_config ();
+ retry:
+ cfg = fc_atomic_ptr_get (&_fcConfig);
+
+ if (config == cfg)
++ {
++ unlock_config ();
++ if (config)
++ FcConfigDestroy (config);
+ return FcTrue;
+-
+- if (config && !config->fonts[FcSetSystem])
+- if (!FcConfigBuildFonts (config))
+- return FcFalse;
++ }
+
+ if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config))
+ goto retry;
+-
+- FcConfigReference (config);
++ unlock_config ();
+ if (cfg)
+ FcConfigDestroy (cfg);
+
+@@ -2649,7 +2707,9 @@ FcConfigSetSysRoot (FcConfig *config,
+ {
+ FcChar8 *s = NULL;
+ FcBool init = FcFalse;
++ int nretry = 3;
+
++retry:
+ if (!config)
+ {
+ /* We can't use FcConfigGetCurrent() here to ensure
+@@ -2681,6 +2741,17 @@ FcConfigSetSysRoot (FcConfig *config,
+ if (init)
+ {
+ config = FcInitLoadOwnConfigAndFonts (config);
++ if (!config)
++ {
++ /* Something failed. this is usually unlikely. so retrying */
++ init = FcFalse;
++ if (--nretry == 0)
++ {
++ fprintf (stderr, "Fontconfig warning: Unable to initialize config and retry limit exceeded. sysroot functionality may not work as expected.\n");
++ return;
++ }
++ goto retry;
++ }
+ FcConfigSetCurrent (config);
+ /* FcConfigSetCurrent() increases the refcount.
+ * decrease it here to avoid the memory leak.
+--
+2.24.1
+