Re: [PATCH v3] merge: fix cache_entry use-after-free
From: David Turner <hidden>
Date: 2016-06-15 23:06:53
On Thu, 2015-10-15 at 05:35 +0200, René Scharfe wrote:
Am 15.10.2015 um 00:07 schrieb David Turner:quoted
From: Keith McGuigan <redacted> During merges, we would previously free entries that we no longer need in the destination index. But those entries might also be stored in the dir_entry cache, and when a later call to add_to_index found them, they would be used after being freed. To prevent this, add a ref count for struct cache_entry. Whenever a cache entry is added to a data structure, the ref count is incremented; when it is removed from the data structure, it is decremented. When it hits zero, the cache_entry is freed. Signed-off-by: Keith McGuigan <redacted> Signed-off-by: David Turner <redacted> --- Fix type of ref_count (from unsigned int to int). cache.h | 27 +++++++++++++++++++++++++++ name-hash.c | 7 ++++++- read-cache.c | 6 +++++- split-index.c | 13 ++++++++----- unpack-trees.c | 6 ++++-- 5 files changed, 50 insertions(+), 9 deletions(-)diff --git a/cache.h b/cache.h index 752031e..7906026 100644 --- a/cache.h +++ b/cache.h@@ -149,6 +149,7 @@ struct stat_data { struct cache_entry { struct hashmap_entry ent; + int ref_count; /* count the number of refs to this in dir_hash */ struct stat_data ce_stat_data; unsigned int ce_mode; unsigned int ce_flags;@@ -213,6 +214,32 @@ struct cache_entry { struct pathspec; /* + * Increment the cache_entry reference count. Should be called + * whenever a pointer to a cache_entry is retained in a data structure, + * thus marking it as alive. + */ +static inline void add_ce_ref(struct cache_entry *ce) +{ + assert(ce != NULL && ce->ref_count >= 0); + ce->ref_count++; +} + +/* + * Decrement the cache_entry reference count. Should be called whenever + * a pointer to a cache_entry is dropped. Once the counter drops to 0 + * the cache_entry memory will be safely freed. + */ +static inline void drop_ce_ref(struct cache_entry *ce) +{ + if (ce != NULL) { + assert(ce->ref_count >= 0);Shouldn't this be "> 0" to prevent double frees?
No. If the ref_count is 1, then there is still some reference to the ce. If it is 0, there is no reference to it, and the next check (< 1) will succeed and the ce will get freed.
quoted
+ if (--ce->ref_count < 1) { + free(ce); + } + } +}