summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Haslam <jason@scitools.com>2015-02-14 23:43:26 -0700
committerJason Haslam <jason@scitools.com>2015-02-14 23:43:26 -0700
commit8588cb0cbfadcd66061062bd0ab170a1419e006f (patch)
tree6dd734ff7a7540cc098e9f1ee7e6770841bf7c70 /src
parent57f45e7f4dd7202607f51292a816a262622e964c (diff)
downloadlibgit2-8588cb0cbfadcd66061062bd0ab170a1419e006f.tar.gz
Fix race in git_packfile_unpack.
Increment refcount of newly added cache entries just like existing entries looked up from the cache. Otherwise the new entry can be evicted from the cache and destroyed while it's still in use.
Diffstat (limited to 'src')
-rw-r--r--src/pack.c11
1 files changed, 9 insertions, 2 deletions
diff --git a/src/pack.c b/src/pack.c
index 47ce854c4..6891a7d03 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -56,6 +56,7 @@ static git_pack_cache_entry *new_cache_object(git_rawobj *source)
if (!e)
return NULL;
+ git_atomic_inc(&e->refcount);
memcpy(&e->raw, source, sizeof(git_rawobj));
return e;
@@ -145,7 +146,11 @@ static void free_lowest_entry(git_pack_cache *cache)
}
}
-static int cache_add(git_pack_cache *cache, git_rawobj *base, git_off_t offset)
+static int cache_add(
+ git_pack_cache_entry **cached_out,
+ git_pack_cache *cache,
+ git_rawobj *base,
+ git_off_t offset)
{
git_pack_cache_entry *entry;
int error, exists = 0;
@@ -171,6 +176,8 @@ static int cache_add(git_pack_cache *cache, git_rawobj *base, git_off_t offset)
assert(error != 0);
kh_value(cache->entries, k) = entry;
cache->memory_used += entry->raw.len;
+
+ *cached_out = entry;
}
git_mutex_unlock(&cache->lock);
/* Somebody beat us to adding it into the cache */
@@ -699,7 +706,7 @@ int git_packfile_unpack(
* long as it's not already the cached one.
*/
if (!cached)
- free_base = !!cache_add(&p->bases, obj, elem->base_key);
+ free_base = !!cache_add(&cached, &p->bases, obj, elem->base_key);
elem = &stack[elem_pos - 1];
curpos = elem->offset;