]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
cache/api: prevent deadlock on kr_cache_remove with multiple processes
authorMarek Vavruša <mvavrusa@cloudflare.com>
Tue, 21 Aug 2018 06:59:24 +0000 (23:59 -0700)
committerMarek Vavruša <mvavrusa@cloudflare.com>
Fri, 7 Sep 2018 17:45:21 +0000 (10:45 -0700)
Only the `kr_cache_remove_subtree` called `kr_cache_sync` to commit
the write transaction after cache removal operation. This wasn't
done in the `kr_cache_remove` so the write transaction could be
long-lived.

With two or more processes, if one help the write transaction open,
no other process could open it. If the process holding the transaction
would call IPC to other processes and wait, it would never release
it and the other processes could never acquire it, and deadlock
would occur.

lib/cache/api.c

index 9f945202766a05cd7dc8e3e8cfac401b8a2eef7e..52e786869248b9a6fab474850926e31a9670e421 100644 (file)
@@ -862,7 +862,12 @@ int kr_cache_remove(struct kr_cache *cache, const knot_dname_t *name, uint16_t t
        if (ret) return kr_error(ret);
 
        knot_db_val_t key = key_exact_type(k, type, NULL);
-       return cache_op(cache, remove, &key, 1);
+       ret = cache_op(cache, remove, &key, 1);
+
+       /* Commit write transactions, as only one can be open at a time */
+       kr_cache_sync(cache);
+
+       return ret;
 }
 
 int kr_cache_match(struct kr_cache *cache, const knot_dname_t *name,
@@ -950,7 +955,8 @@ int kr_cache_remove_subtree(struct kr_cache *cache, const knot_dname_t *name,
        }
        ret = cache->api->remove(cache->db, keys, count);
 cleanup:
-       kr_cache_sync(cache); /* Sync even after just kr_cache_match(). */
+       /* Commit write transactions, as only one can be open at a time */
+       kr_cache_sync(cache);
        /* Free keys */
        while (--i >= 0) {
                free(keys[i].data);