diff options
author | Milan Crha <mcrha@redhat.com> | 2020-10-21 22:36:45 +0200 |
---|---|---|
committer | Allen Winter <allen.winter@kdab.com> | 2021-01-09 09:15:10 -0500 |
commit | 903248f5e4d8e5ea0b23714535bdc9b174b571b4 (patch) | |
tree | 4b067976364855375a48b38bb828015abe076d64 /src | |
parent | 1a62ceb499f48f82eef538cff81bcd915695f07c (diff) | |
download | libical-git-903248f5e4d8e5ea0b23714535bdc9b174b571b4.tar.gz |
Improve thread safety of icaltimezone_load_builtin_timezone()
Even the function does test whether the passed-in zone has set
the component, it doesn't retest it when it acquires the lock, but
other thread could already assign the component, which can cause
use-after-free in certain thread interleaving.
Signed-off-by: Allen Winter <allen.winter@kdab.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/libical/icaltimezone.c | 6 | ||||
-rw-r--r-- | src/test/builtin_timezones.c | 57 |
2 files changed, 63 insertions, 0 deletions
diff --git a/src/libical/icaltimezone.c b/src/libical/icaltimezone.c index d1c3b296..ff921ddb 100644 --- a/src/libical/icaltimezone.c +++ b/src/libical/icaltimezone.c @@ -1810,6 +1810,12 @@ static void icaltimezone_load_builtin_timezone(icaltimezone *zone) icaltimezone_builtin_lock(); + /* Try again, maybe it had been set by other thread while waiting for the lock */ + if (zone->component) { + icaltimezone_builtin_unlock(); + return; + } + /* If the location isn't set, it isn't a builtin timezone. */ if (!zone->location || !zone->location[0]) { icaltimezone_builtin_unlock(); diff --git a/src/test/builtin_timezones.c b/src/test/builtin_timezones.c index 5a24a52d..08f793d3 100644 --- a/src/test/builtin_timezones.c +++ b/src/test/builtin_timezones.c @@ -20,10 +20,63 @@ #include <config.h> #endif +#ifdef HAVE_PTHREAD_H +#include <pthread.h> +#include <assert.h> +#endif + #include "libical/ical.h" #include <stdio.h> +#if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_CREATE) + +#define N_THREADS 20 + +static pthread_mutex_t thread_comp_mutex = PTHREAD_MUTEX_INITIALIZER; +static const void *thread_comp = NULL; + +static void * +thread_func(void *user_data) +{ + icaltimezone *zone = user_data; + icalcomponent *icalcomp; + + if(!zone) + return NULL; + + icalcomp = icaltimezone_get_component(zone); + pthread_mutex_lock(&thread_comp_mutex); + if(!thread_comp) + thread_comp = icalcomp; + else + assert(thread_comp == icalcomp); + pthread_mutex_unlock(&thread_comp_mutex); + icalcomp = icalcomponent_new_clone(icalcomp); + icalcomponent_free(icalcomp); + + return NULL; +} + +static void +test_get_component_threadsafety(void) +{ + pthread_t thread[N_THREADS]; + icaltimezone *zone; + int ii; + + zone = icaltimezone_get_builtin_timezone("Europe/London"); + + for(ii = 0; ii < N_THREADS; ii++) { + pthread_create(&thread[ii], NULL, thread_func, zone); + } + + for(ii = 0; ii < N_THREADS; ii++) { + pthread_join(thread[ii], NULL); + } +} +#endif + int main() { icalarray *builtin_timezones; @@ -34,6 +87,10 @@ int main() set_zone_directory("../../zoneinfo"); icaltimezone_set_tzid_prefix("/softwarestudio.org/"); + #if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_CREATE) + test_get_component_threadsafety(); + #endif + tt = icaltime_current_time_with_zone(icaltimezone_get_builtin_timezone("America/New_York")); tt.year = 2038; |