Fix potential race condition in FcConfigSetCurrent and FcConfigReference

This commit is contained in:
Akira TAGOH 2019-11-02 00:14:48 +09:00
parent b5bcf61fe7
commit aa8c8cfa9f
2 changed files with 90 additions and 17 deletions

View File

@ -1085,6 +1085,8 @@ FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat)
if (fd < 0) if (fd < 0)
return NULL; return NULL;
config = FcConfigReference (NULL); config = FcConfigReference (NULL);
if (!config)
return NULL;
cache = FcDirCacheMapFd (config, fd, file_stat, NULL); cache = FcDirCacheMapFd (config, fd, file_stat, NULL);
FcConfigDestroy (config); FcConfigDestroy (config);
close (fd); close (fd);

View File

@ -33,6 +33,49 @@
#endif #endif
static FcConfig *_fcConfig; /* MT-safe */ 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 * static FcConfig *
FcConfigEnsure (void) FcConfigEnsure (void)
@ -44,7 +87,8 @@ retry:
{ {
config = FcInitLoadConfigAndFonts (); config = FcInitLoadConfigAndFonts ();
if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) { if (!config || !fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
if (config)
FcConfigDestroy (config); FcConfigDestroy (config);
goto retry; goto retry;
} }
@ -76,6 +120,7 @@ FcConfigFini (void)
FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig); FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig);
if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL)) if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
FcConfigDestroy (cfg); FcConfigDestroy (cfg);
free_lock ();
} }
static FcChar8 * static FcChar8 *
@ -297,26 +342,30 @@ FcConfigReference (FcConfig *config)
{ {
if (!config) if (!config)
{ {
/* Do not use FcConfigGetCurrent () for the purpose of obtaining current FcConfig here. /* lock during obtaining the value from _fcConfig and count up refcount there,
* because the reference counter must be increased before setting it to _fcConfig. * there are the race between them.
*/ */
lock_config ();
retry: retry:
config = fc_atomic_ptr_get (&_fcConfig); config = fc_atomic_ptr_get (&_fcConfig);
if (!config) if (!config)
{ {
config = FcConfigCreate (); unlock_config ();
FcRefInc (&config->ref);
config = FcInitLoadOwnConfigAndFonts (config); config = FcInitLoadConfigAndFonts ();
if (!config)
goto retry;
lock_config ();
if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config))
{ {
FcConfigDestroy (config); /* To decrease the refcount for the above one. */ FcConfigDestroy (config);
FcConfigDestroy (config); /* To destroy it actualy */
goto retry; goto retry;
} }
return config;
} }
FcRefInc (&config->ref);
unlock_config ();
} }
else
FcRefInc (&config->ref); FcRefInc (&config->ref);
return config; return config;
@ -529,20 +578,29 @@ FcConfigSetCurrent (FcConfig *config)
{ {
FcConfig *cfg; FcConfig *cfg;
if (config)
{
if (!config->fonts[FcSetSystem])
if (!FcConfigBuildFonts (config))
return FcFalse;
FcRefInc (&config->ref);
}
lock_config ();
retry: retry:
cfg = fc_atomic_ptr_get (&_fcConfig); cfg = fc_atomic_ptr_get (&_fcConfig);
if (config == cfg) if (config == cfg)
{
unlock_config ();
if (config)
FcConfigDestroy (config);
return FcTrue; return FcTrue;
}
if (config && !config->fonts[FcSetSystem])
if (!FcConfigBuildFonts (config))
return FcFalse;
if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config)) if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config))
goto retry; goto retry;
unlock_config ();
FcConfigReference (config);
if (cfg) if (cfg)
FcConfigDestroy (cfg); FcConfigDestroy (cfg);
@ -2649,7 +2707,9 @@ FcConfigSetSysRoot (FcConfig *config,
{ {
FcChar8 *s = NULL; FcChar8 *s = NULL;
FcBool init = FcFalse; FcBool init = FcFalse;
int nretry = 3;
retry:
if (!config) if (!config)
{ {
/* We can't use FcConfigGetCurrent() here to ensure /* We can't use FcConfigGetCurrent() here to ensure
@ -2681,6 +2741,17 @@ FcConfigSetSysRoot (FcConfig *config,
if (init) if (init)
{ {
config = FcInitLoadOwnConfigAndFonts (config); 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 (config);
/* FcConfigSetCurrent() increases the refcount. /* FcConfigSetCurrent() increases the refcount.
* decrease it here to avoid the memory leak. * decrease it here to avoid the memory leak.