]> git.ipfire.org Git - thirdparty/git.git/commit
receive-pack: fix use-after-free bug
authorÆvar Arnfjörð Bjarmason <avarab@gmail.com>
Wed, 20 Feb 2019 00:00:33 +0000 (01:00 +0100)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Feb 2019 23:02:12 +0000 (15:02 -0800)
commit9903623761e6882a1c885ca4245dd6951052ecc3
tree46fe33be9120bd2c04c16ad244d56ca70c5a3424
parent268fbcd172cdb306e8a3e7143cc16677c963d6cd
receive-pack: fix use-after-free bug

The resolve_ref_unsafe() function can, and sometimes will in the case
of this codepath, return the char * passed to it to the caller. In
this case we construct a strbuf, free it, and then continue using the
dst_name after that free().

The code being fixed dates back to da3efdb17b ("receive-pack: detect
aliased updates which can occur with symrefs", 2010-04-19). When it
was originally added it didn't have this bug, it was introduced when
it was subsequently modified to use strbuf in 6b01ecfe22 ("ref
namespaces: Support remote repositories via upload-pack and
receive-pack", 2011-07-08).

This is theoretically a security issue, the C standard makes no
guarantees that a value you use after free() hasn't been poked at or
changed by something else on the system, but in practice modern OSs
will have mapped the relevant page to this process, so nothing else
would have used it. We do no further allocations between the free()
and use-after-free, so we ourselves didn't corrupt or change the
value.

Jeff investigated that and found: "It probably would be an issue if
the allocation were larger. glibc at least will use mmap()/munmap()
after some cutoff[1], in which case we'd get a segfault from hitting
the unmapped page. But for small allocations, it just bumps brk() and
the memory is still available for further allocations after
free(). [...] If you had a sufficiently large refname you might be
able to trigger the bug [...]. I tried to push such a ref. I had to
manually make a packed-refs file with the long name to avoid
filesystem limits (though probably you could have a long a/b/c/ name
on ext4).  But the result can't actually be pushed, because it all has
to fit into a 64k pkt-line as part of the push protocol.".

An a alternative and more succinct way of implementing this would have
been to do the strbuf_release() at the end of check_aliased_update()
and use "goto out" instead of the early "return" statements. Hopefully
this approach of using a helper instead makes it easier to follow.

1. Jeff: "Weirdly, the mmap() cutoff on my glibc system is 135168
   bytes. Which is...2^17 + 2^12? 33 pages? I'm sure there's a good
   reason for that, but I didn't dig into it."

Reported-by: 王健强 <jianqiang.wang@securitygossip.com>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/receive-pack.c