From: René Scharfe Date: Thu, 15 Oct 2015 03:35:01 GMT Subject: Re: [PATCH v3] merge: fix cache_entry use-after-free Message-ID: <561F1EE5.5070300@web.de> In-Reply-To: <1444860424-8361-1-git-send-email-dturner@twopensource.com> Am 15.10.2015 um 00:07 schrieb David Turner: > From: Keith McGuigan > > 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 > Signed-off-by: David Turner > --- > > 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? > + if (--ce->ref_count < 1) { > + free(ce); > + } > + } > +}