diff options
author | Paul J. Davis <paul.joseph.davis@gmail.com> | 2019-12-17 11:40:24 -0600 |
---|---|---|
committer | Paul J. Davis <paul.joseph.davis@gmail.com> | 2019-12-17 13:53:57 -0600 |
commit | 850a9934c8cd3eb4ebc0100880ebad47978c46f3 (patch) | |
tree | 8872e11cb1c9251519b880279e92f95cfea7600c | |
parent | de50b07856ae3c4dd42d508e955c112c43721950 (diff) | |
download | couchdb-850a9934c8cd3eb4ebc0100880ebad47978c46f3.tar.gz |
Fix use after free of ICU collators
During `init:restart()` we end up calling the `couch_ejson_compare`
NIF's unload function which destroys all of the allocated collators.
However, we don't clear the associated thread local states which leads
us to a use after free issue and the ensuing segfaults. This adds checks
so that threads know when their cached threadlocal collator is no longer
valid.
-rw-r--r-- | src/couch/priv/couch_ejson_compare/couch_ejson_compare.c | 10 |
1 files changed, 9 insertions, 1 deletions
diff --git a/src/couch/priv/couch_ejson_compare/couch_ejson_compare.c b/src/couch/priv/couch_ejson_compare/couch_ejson_compare.c index 6d1043fa4..ad3d0cdd6 100644 --- a/src/couch/priv/couch_ejson_compare/couch_ejson_compare.c +++ b/src/couch/priv/couch_ejson_compare/couch_ejson_compare.c @@ -48,9 +48,11 @@ typedef struct { } ctx_t; static threadlocal UCollator* collator = NULL; +static threadlocal int64_t threadEpoch = 0; static UCollator** collators = NULL; static int numCollators = 0; static int numSchedulers = 0; +static int64_t loadEpoch = 0; static ErlNifMutex* collMutex = NULL; static ERL_NIF_TERM less_json_nif(ErlNifEnv*, int, const ERL_NIF_TERM []); @@ -69,7 +71,7 @@ get_collator() { UErrorCode status = U_ZERO_ERROR; - if(collator != NULL) { + if(collator != NULL && threadEpoch == loadEpoch) { return collator; } @@ -87,6 +89,8 @@ get_collator() assert(numCollators <= numSchedulers && "Number of schedulers shrank."); + threadEpoch = loadEpoch; + return collator; } @@ -387,6 +391,8 @@ on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) return 2; } + loadEpoch += 1; + collMutex = enif_mutex_create("coll_mutex"); if (collMutex == NULL) { @@ -421,6 +427,8 @@ on_unload(ErlNifEnv* env, void* priv_data) enif_free(collators); } + numCollators = 0; + if (collMutex != NULL) { enif_mutex_destroy(collMutex); } |