184 lines
8.2 KiB
Diff
184 lines
8.2 KiB
Diff
From 81dc898df9b4b4035534a927f3234a3839b698bf Mon Sep 17 00:00:00 2001
|
|
From: Patrick Steinhardt <ps@pks.im>
|
|
Date: Thu, 1 Dec 2022 15:46:25 +0100
|
|
Subject: [PATCH] pretty: fix out-of-bounds write caused by integer overflow
|
|
|
|
When using a padding specifier in the pretty format passed to git-log(1)
|
|
we need to calculate the string length in several places. These string
|
|
lengths are stored in `int`s though, which means that these can easily
|
|
overflow when the input lengths exceeds 2GB. This can ultimately lead to
|
|
an out-of-bounds write when these are used in a call to memcpy(3P):
|
|
|
|
==8340==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f1ec62f97fe at pc 0x7f2127e5f427 bp 0x7ffd3bd63de0 sp 0x7ffd3bd63588
|
|
WRITE of size 1 at 0x7f1ec62f97fe thread T0
|
|
#0 0x7f2127e5f426 in __interceptor_memcpy /usr/src/debug/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827
|
|
#1 0x5628e96aa605 in format_and_pad_commit pretty.c:1762
|
|
#2 0x5628e96aa7f4 in format_commit_item pretty.c:1801
|
|
#3 0x5628e97cdb24 in strbuf_expand strbuf.c:429
|
|
#4 0x5628e96ab060 in repo_format_commit_message pretty.c:1869
|
|
#5 0x5628e96acd0f in pretty_print_commit pretty.c:2161
|
|
#6 0x5628e95a44c8 in show_log log-tree.c:781
|
|
#7 0x5628e95a76ba in log_tree_commit log-tree.c:1117
|
|
#8 0x5628e922bed5 in cmd_log_walk_no_free builtin/log.c:508
|
|
#9 0x5628e922c35b in cmd_log_walk builtin/log.c:549
|
|
#10 0x5628e922f1a2 in cmd_log builtin/log.c:883
|
|
#11 0x5628e9106993 in run_builtin git.c:466
|
|
#12 0x5628e9107397 in handle_builtin git.c:721
|
|
#13 0x5628e9107b07 in run_argv git.c:788
|
|
#14 0x5628e91088a7 in cmd_main git.c:923
|
|
#15 0x5628e939d682 in main common-main.c:57
|
|
#16 0x7f2127c3c28f (/usr/lib/libc.so.6+0x2328f)
|
|
#17 0x7f2127c3c349 in __libc_start_main (/usr/lib/libc.so.6+0x23349)
|
|
#18 0x5628e91020e4 in _start ../sysdeps/x86_64/start.S:115
|
|
|
|
0x7f1ec62f97fe is located 2 bytes to the left of 4831838265-byte region [0x7f1ec62f9800,0x7f1fe62f9839)
|
|
allocated by thread T0 here:
|
|
#0 0x7f2127ebe7ea in __interceptor_realloc /usr/src/debug/gcc/libsanitizer/asan/asan_malloc_linux.cpp:85
|
|
#1 0x5628e98774d4 in xrealloc wrapper.c:136
|
|
#2 0x5628e97cb01c in strbuf_grow strbuf.c:99
|
|
#3 0x5628e97ccd42 in strbuf_addchars strbuf.c:327
|
|
#4 0x5628e96aa55c in format_and_pad_commit pretty.c:1761
|
|
#5 0x5628e96aa7f4 in format_commit_item pretty.c:1801
|
|
#6 0x5628e97cdb24 in strbuf_expand strbuf.c:429
|
|
#7 0x5628e96ab060 in repo_format_commit_message pretty.c:1869
|
|
#8 0x5628e96acd0f in pretty_print_commit pretty.c:2161
|
|
#9 0x5628e95a44c8 in show_log log-tree.c:781
|
|
#10 0x5628e95a76ba in log_tree_commit log-tree.c:1117
|
|
#11 0x5628e922bed5 in cmd_log_walk_no_free builtin/log.c:508
|
|
#12 0x5628e922c35b in cmd_log_walk builtin/log.c:549
|
|
#13 0x5628e922f1a2 in cmd_log builtin/log.c:883
|
|
#14 0x5628e9106993 in run_builtin git.c:466
|
|
#15 0x5628e9107397 in handle_builtin git.c:721
|
|
#16 0x5628e9107b07 in run_argv git.c:788
|
|
#17 0x5628e91088a7 in cmd_main git.c:923
|
|
#18 0x5628e939d682 in main common-main.c:57
|
|
#19 0x7f2127c3c28f (/usr/lib/libc.so.6+0x2328f)
|
|
#20 0x7f2127c3c349 in __libc_start_main (/usr/lib/libc.so.6+0x23349)
|
|
#21 0x5628e91020e4 in _start ../sysdeps/x86_64/start.S:115
|
|
|
|
SUMMARY: AddressSanitizer: heap-buffer-overflow /usr/src/debug/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827 in __interceptor_memcpy
|
|
Shadow bytes around the buggy address:
|
|
0x0fe458c572a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
|
0x0fe458c572b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
|
0x0fe458c572c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
|
0x0fe458c572d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
|
0x0fe458c572e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
|
=>0x0fe458c572f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa[fa]
|
|
0x0fe458c57300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
0x0fe458c57310: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
0x0fe458c57320: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
0x0fe458c57330: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
0x0fe458c57340: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
|
Shadow byte legend (one shadow byte represents 8 application bytes):
|
|
Addressable: 00
|
|
Partially addressable: 01 02 03 04 05 06 07
|
|
Heap left redzone: fa
|
|
Freed heap region: fd
|
|
Stack left redzone: f1
|
|
Stack mid redzone: f2
|
|
Stack right redzone: f3
|
|
Stack after return: f5
|
|
Stack use after scope: f8
|
|
Global redzone: f9
|
|
Global init order: f6
|
|
Poisoned by user: f7
|
|
Container overflow: fc
|
|
Array cookie: ac
|
|
Intra object redzone: bb
|
|
ASan internal: fe
|
|
Left alloca redzone: ca
|
|
Right alloca redzone: cb
|
|
==8340==ABORTING
|
|
|
|
The pretty format can also be used in `git archive` operations via the
|
|
`export-subst` attribute. So this is what in our opinion makes this a
|
|
critical issue in the context of Git forges which allow to download an
|
|
archive of user supplied Git repositories.
|
|
|
|
Fix this vulnerability by using `size_t` instead of `int` to track the
|
|
string lengths. Add tests which detect this vulnerability when Git is
|
|
compiled with the address sanitizer.
|
|
|
|
Reported-by: Joern Schneeweisz <jschneeweisz@gitlab.com>
|
|
Original-patch-by: Joern Schneeweisz <jschneeweisz@gitlab.com>
|
|
Modified-by: Taylor Blau <me@ttalorr.com>
|
|
Signed-off-by: Patrick Steinhardt <ps@pks.im>
|
|
Signed-off-by: Junio C Hamano <gitster@pobox.com>
|
|
---
|
|
pretty.c | 11 ++++++-----
|
|
t/t4205-log-pretty-formats.sh | 17 +++++++++++++++++
|
|
2 files changed, 23 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/pretty.c b/pretty.c
|
|
index 7a7708a0ea..a1a01492c1 100644
|
|
--- a/pretty.c
|
|
+++ b/pretty.c
|
|
@@ -1473,7 +1473,9 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
|
|
struct format_commit_context *c)
|
|
{
|
|
struct strbuf local_sb = STRBUF_INIT;
|
|
- int total_consumed = 0, len, padding = c->padding;
|
|
+ size_t total_consumed = 0;
|
|
+ int len, padding = c->padding;
|
|
+
|
|
if (padding < 0) {
|
|
const char *start = strrchr(sb->buf, '\n');
|
|
int occupied;
|
|
@@ -1485,7 +1487,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
|
|
}
|
|
while (1) {
|
|
int modifier = *placeholder == 'C';
|
|
- int consumed = format_commit_one(&local_sb, placeholder, c);
|
|
+ size_t consumed = format_commit_one(&local_sb, placeholder, c);
|
|
total_consumed += consumed;
|
|
|
|
if (!modifier)
|
|
@@ -1551,7 +1553,7 @@ static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
|
|
}
|
|
strbuf_addbuf(sb, &local_sb);
|
|
} else {
|
|
- int sb_len = sb->len, offset = 0;
|
|
+ size_t sb_len = sb->len, offset = 0;
|
|
if (c->flush_type == flush_left)
|
|
offset = padding - len;
|
|
else if (c->flush_type == flush_both)
|
|
@@ -1574,8 +1576,7 @@ static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
|
|
const char *placeholder,
|
|
void *context)
|
|
{
|
|
- int consumed;
|
|
- size_t orig_len;
|
|
+ size_t consumed, orig_len;
|
|
enum {
|
|
NO_MAGIC,
|
|
ADD_LF_BEFORE_NON_EMPTY,
|
|
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
|
|
index 204c149d5a..fff3e05615 100755
|
|
--- a/t/t4205-log-pretty-formats.sh
|
|
+++ b/t/t4205-log-pretty-formats.sh
|
|
@@ -867,4 +867,21 @@ test_expect_success 'log --pretty=reference is colored appropriately' '
|
|
test_cmp expect actual
|
|
'
|
|
|
|
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message' '
|
|
+ # We only assert that this command does not crash. This needs to be
|
|
+ # executed with the address sanitizer to demonstrate failure.
|
|
+ git log -1 --pretty="format:%>(2147483646)%x41%41%>(2147483646)%x41" >/dev/null
|
|
+'
|
|
+
|
|
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'set up huge commit' '
|
|
+ test-tool genzeros 2147483649 | tr "\000" "1" >expect &&
|
|
+ huge_commit=$(git commit-tree -F expect HEAD^{tree})
|
|
+'
|
|
+
|
|
+test_expect_success EXPENSIVE,SIZE_T_IS_64BIT 'log --pretty with huge commit message' '
|
|
+ git log -1 --format="%B%<(1)%x30" $huge_commit >actual &&
|
|
+ echo 0 >>expect &&
|
|
+ test_cmp expect actual
|
|
+'
|
|
+
|
|
test_done
|
|
--
|
|
2.27.0
|
|
|