Gitlab Security Audit
Gitlab Security Audit
2023-01-17
PUBLIC
https://x41-dsec.de/
[email protected]
Contents
1 Summary 4
2 Introduction 7
2.1 Methodology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Findings Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4 Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.5 Recommended Further Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4 Results 15
4.1 Findings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.2 Informational Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
A Fuzzing 88
Dashboard
Target
Customer Open Source Technology Improvement Fund (OSTIF)
Name Git
Type Source Code
Version 2.38.1
Engagement
Type Source Code Review
Consultants 3: Eric Sesterhenn, Joern Schneeweisz, and Markus Vervier
41 person days, 29 sponsored by OSTIF, 12 by GitLab,
Engagement Effort
2022-11-01 to 2022-12-09
Critical - 2
CWE-1088 (1)
Medium - 1
CWE-680 (1)
None - 27
0 2 4 6 8 10 12 14 16 18 20 22 24 26
1 Summary
In November and December 2022, X41 D-Sec GmbH performed a security source code audit
against the Git to identify security issues. The test was organized by the Open Source Technology
-Improvement Fund1 as a concerted effort involving multiple teams. GitLab2 directly supported
the assessment by sponsoring participation of the GitLab Security Research Team3 in the audit.
A total of eight vulnerabilities were discovered during the test by the team. Two were rated as
critical, one was classified as high severity, one as medium, and four as low. Additionally, 27
issues without a direct security impact were identified.
Medium - 1
High - 1
Low - 4
Critical - 2
1 https://ostif.org
2 https://about.gitlab.com
3 https://about.gitlab.com/handbook/security/threat-management/security-research/
Git is a distributed version control system that allows developers to collaborate on software de-
velopment. It is integrated into popular packaging systems, including Golang modules, Rust cargo,
and NodeJS NPM. A vulnerability in Git could potentially allow attackers to compromise source
code repositories or developer systems. In a hypothetical scenario, a wormable vulnerability in
Git could result in security breaches on a large scale.
The source code of Git was inspected for vulnerabilities by security experts Eric Sesterhenn (X41),
Joern Schneeweisz (GitLab), and Markus Vervier (X41) using manual code review, code analysis
tools, and custom fuzzing efforts.
The most severe issue discovered allows an attacker to trigger a heap-based memory corruption
during clone or pull operations, which might result in code execution. Another critical issue al-
lows code execution during an archive operation, which is commonly performed by Git forges.
Additionally, a huge number of integer related issues was identified which may lead to denial-of-
service situations, out-of-bound reads or simply badly handled corner cases on large input.
On a positive note, the use of many short-running processes reduces the impact of memory
disclosure issues and allows for an error-handling where the operating system performs most
cleanups. This greatly reduces the amount of anti-patterns for common issues usually found
in C programs such as use-after-free issues. Additionally, the use of a stringbuffer wrapper to
perform string operations and that functions often leading to security errors are disallowed (such
as strcpy()) has a positive effect on the overall security.
On another positive note, it was visible that the software has been subject to improvements that
protect against logical errors when it comes to processing untrustworthy contents from remote
repositories.
Given the size of the Git codebase, finding each potential instance of memory safety issues would
be a significant undertaking, not possible in the time given for this review. To address this, we
recommend extending the use of safe wrappers and developing strategies to mitigate common
memory safety issues. Introducing generic hardenings such as sanity checks on data input length
and the use of safe wrappers can improve the security of the software in the short term. The
usage of signed integer typed variables to store length values should be banned. Additionally, the
software could benefit from compiler level checks regarding the use of integer and long variable
types for length and size values. Enabling the related compiler warnings during the build process
can help identify the issues early in the development process. Finally, improving the custom error
handling can enable better analysis of the code with tools like Valgrind or memory leak checkers.
In conclusion, the Git codebase shows several security issues and the sheer size of the codebase
makes it challenging to address all potential instances of these issues. The use of safe wrap-
pers can improve the overall security of the software as a short term strategy. As a long term
2 Introduction
X41 reviewed the source code of the Git free and open source distributed version control system.
It allows developers to share source code and track changes made by others and designed to
handle everything from small to very large projects with speed and efficiency.
Git is a commonly used version control system in the software industry, and is integrated into
various software ecosystems such as Golang, Rust, and NodeJS/NPM. As a result, security vulner-
abilities in Git can have far-reaching effects on individuals and organizations. To protect against
these potential risks, it is essential to address and fix any exploitable security issues in Git.
Being exposed by design to untrustworthy data, attackers could try to attack repositories both
upstream and downstream by uploading malicious data, attacking the Git protocol, or exploit
issues in the interaction with transport protocols such as HTTP1 or SSH2 . Logic issues might
be exploited which could allow tampering with the integrity of repositories or to gain access to
protected repositories.
Given the nature of Git and it’s integration into development and software distribution processes,
code execution vulnerabilities could even become wormable.
2.1 Methodology
A manual approach for penetration testing and for code review is used by X41. This process is
supported by tools such as static code analyzers and industry standard web application security
tools3 . The review process is performed in the following steps:
1. Identify the scope of the review: We work closely with our clients to understand their goals
and objectives for the source code security review. This helps us define the scope of the
1 HyperText Transfer Protocol
2 Secure Shell
3 https://portswigger.net/burp
review and ensure that we are focused on the areas of the code that are most relevant to
our client’s needs.
2. Familiarize ourselves with the codebase: Before we begin the review, we take the time to
familiarize ourselves with the codebase. This involves reading through the code and gaining
a high-level understanding of how it is structured and how it functions.
3. Develop a review plan: Based on our understanding of the codebase and the goals of the
review, we develop a detailed review plan that outlines the specific steps we will take to
conduct the review. This plan typically includes a combination of manual code analysis and
the use of specialized tools to automate parts of the review process.
4. Conduct the review: Using the review plan as a guide, we carefully review the source code
for potential vulnerabilities. This involves a combination of manual code analysis and the
use of specialized tools to automate parts of the review process.
5. Report our findings: After completing the review, we compile our findings into a compre-
hensive report. This report includes a detailed summary of the vulnerabilities we identified,
along with recommendations for how to address them. We work closely with our clients
to ensure that they understand our findings and can take appropriate steps to improve the
security of their source code.
Our team of experienced security consultants uses its knowledge and expertise to identify po-
tential vulnerabilities in source code and provides recommendations for how to address them.
X41 adheres to established standards for source code reviewing and penetration testing. These
are in particular the CERT Secure Coding4 standards and the Study - A Penetration Testing Model5
of the German Federal Office for Information Security.
4https://wiki.sei.cmu.edu/confluence/display/seccode/SEI+CERT+Coding+Standards
5https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Studies/Penetration/penetrati
on_pdf.pdf?__blob=publicationFile&v=1
2.3 Scope
X41 reviewed the Git source code version 2.38.16 with a focus on the core components written
in C. The audit was based on a security source code review.
The project consists of around 250.000 lines of C source code with additional tools in Bash, Perl
and other programming languages.
The main target for the audit were 64-bit Linux systems acting as Git client or server. With the
possibility of attacks via malicious clients and repositories.
2.4 Coverage
A security assessment attempts to find the most important or sometimes as many of the existing
problems as possible, though it is practically never possible to rule out the possibility of additional
weaknesses being found in the future.
The time allocated to X41 for this assessment was not sufficient to yield a good coverage of the
given scope.
X41 audited the source code for common defects usually found in C source code manually and
with the help of static analyzers such as LLVM7 , weggli8 , cppcheck9 , semgrep10 , infer11 , joern12
and gcc13 .
Variant analysis for vulnerabilities that were found during the test was performed using the tools
mentioned above.
Whenever code was spotted during the audit which might be a suitable fuzzing target, a har-
ness was created to test these code paths. LLVM libfuzzer was run against parse_attr_line(),
url_decode_mem()/url_decode()/url_percent_decode() and credential_from_url_gently() as well as the
included fuzzers. AFL++14 was used for file-based fuzzing against git-apply and git-status
(after replacing zlib operations with a noop memcpy() to efficiently target pack files). Fuzzing
using AFL++ without that patch was performed in a very limited scope against git-mailinfo.
The formatting parameter of git-log was fuzzed as well using AFL++ since various issues have
6 https://github.com/git/git/releases/tag/v2.38.1
7 https://clang-analyzer.llvm.org/
8 https://github.com/googleprojectzero/weggli
9 https://github.com/danmar/cppcheck
10 https://semgrep.dev/
11 https://fbinfer.com/
12 https://joern.io/
13 https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Static-Analyzer-Options.html
14 https://github.com/AFLplusplus/AFLplusplus
been identified in the handling of this parameter. git-fast-import was fuzz tested using AFL++
as well, after removing the write operations to keep the target repository in a clean state. See
appendix A for a more detailed description of the various test cases performed during the assess-
ment.
The source code was audited for common security issues found in C code such as calculation
overflows and errors in memory management. Additionally, security vulnerabilities related to un-
derlying system level attack surface such as symlink attacks and dangerous arguments passed to
run_command() were inspected for possibilities of argument or command injection. Operations
and processes performed via Git were inspected for logic issues and common usages were in-
spected for potential security issues. Input via network or attacker controlled configuration files
was audited for security issues. The code was audited for integer overflows, but only the most
prominent instances could be reviewed in-depth, due to the huge amount of potential for integer
truncation and overflows in the code base.
Suggestions for next steps in securing this scope can be found in section 2.5.
X41 recommends to mitigate the issues described in this report. Afterwards, CVE15 IDs16 should
be requested and users be informed (e.g. via a changelog or a special note for issues with higher
severity) to ensure that they can make an informed decision about upgrading or other possible
mitigations.
Due to the fact that the audit was focused on Git on 64-bit Linux systems, X41 recommends to
audit the code with a focus on Windows systems as well.
It is recommended to perform a second iteration of this security audit after the code of Git has
been hardened and refactored. X41 recommends to focus on the following areas:
Due to the huge number of places where int or unsigned long types were used for size calcu-
lations, X41 recommends to refactor the code base to use size_t. In general we recommend to
ban the usage of signed integer types for length values, where possible. Even though the POSIX17
API18 requires such types sometimes, the usage can be avoided in most places. Furthermore, the
appropriate compiler warnings should be enabled to identify these and other issues early in the
development process.
The code relies heavily on the operating system to clean up opened file handles and allocated
15 Common Vulnerabilities and Exposures
16 Identifiers
17 Portable Operating System Interface
18 Application Programming Interface
memory in case of errors. This makes testing with leak sanitizers nearly impossible. It is therefore
advised to clean up memory and opened handles in error cases to allow for better testing via
fuzzing or other analysis methods.
Since most issues that rely on overflowing sizes rooted from large amounts of data being stored
in buffers, setting the GIT_ALLOC_LIMIT environment variable to less than 2 gigabyte might mit-
igate some of these until a proper patch is available.
Additionally, more fuzz testing can be applied to the code base to identify further issues in parsing
code.
Since the refactoring mentioned above can take a very long time, given the amount of code and
complexity, it is recommended to perform the subsequent audit after a time boxed refactoring
effort. This will ensure that additional vulnerabilities could be found in the near future already.
Security vulnerabilities are given a purely technical rating by the testers as they are discovered
during the test. Business factors and financial risks for Open Source Technology Improvement
Fund (OSTIF) are beyond the scope of a penetration test which focuses entirely on technical
factors. Yet technical results from a penetration test may be an integral part of a general risk
assessment. A penetration test is based on a limited time frame and only covers vulnerabilities
and security issues which have been found in the given time, there is no claim for full coverage.
Severity Rating
None
Low
Medium
High
Critical
A low rating indicates that the vulnerability is either very hard for an attacker to exploit due
to special circumstances, or that the impact of exploitation is limited, whereas findings with a
medium rating are more likely to be exploited or have a higher impact. High and critical ratings
are assigned when the testers deem the prerequisites realistic or trivial and the impact significant
or very significant.
Findings with the rating ‘none’ are called informational findings and are related to security harden-
ing, affect functionality, or other topics that are not directly related to security. X41 recommends
to mitigate these issues as well, because they often become exploitable in the future. Doing so
will strengthen the security of the system and is recommended for defense in depth.
The CWE1 is a set of software weaknesses that allows the categorization of vulnerabilities and
weaknesses in software. If applicable, X41 provides the CWE-ID for each vulnerability that is
discovered during a test.
CWE is a very powerful method to categorize a vulnerability and to give general descriptions and
solution advice on recurring vulnerability types. CWE is developed by MITRE2 . More information
can be found on the CWE website at https://cwe.mitre.org/.
4 Results
This chapter describes the results of this test. The security-relevant findings are documented in
Section 4.1. Additionally, findings without a direct security impact are documented in Section 4.2.
4.1 Findings
The following subsections describe findings with a direct security impact that were discovered
during the test.
Severity: LOW
CWE: 400 – Uncontrolled Resource Consumption (’Resource Exhaustion’)
Affected Component: strbuf.c:strbuf_split_buf()
4.1.1.1 Description
The function strbuf_split_buf() splits an input string buffer into multiple output string buffers.
When the input string contains mostly terminator symbols, the allocation overhead for small
sized string buffers becomes significant:
8 while (slen) {
9 int len = slen;
10 if (max <= 0 || nr + 1 < max) {
11 const char *end = memchr(str, terminator, slen);
12 if (end)
13 len = end - str + 1;
14 }
15 t = xmalloc(sizeof(struct strbuf));
16 strbuf_init(t, len);
17 strbuf_add(t, str, len);
18 ALLOC_GROW(ret, nr + 2, alloc);
19 ret[nr++] = t;
20 str += len;
21 slen -= len;
22 }
23 ALLOC_GROW(ret, nr + 1, alloc); /* In case string was empty */
24 ret[nr] = NULL;
25 return ret;
26 }
Another issue in this function is the use of int for len. In case slen is big enough, casting it to
an int will truncate it and set len to 0. In this case the loop is unbounded since slen is not
reduced when the string does not contain a terminator symbol. This causes the loop to continue
until all memory is used for the stringbuffer allocations. The CPU1 overhead will be higher in this
case, since memchr() will search the entire 4GB for the terminator symbol in each iteration.
By repeating the b: lines, an attacker is able to consume an arbitrary amount of memory in the
target process. On a test system a 30MB file constructed in that way caused an allocation of
2.5GB:
1 b:
2 b:
3 b:
4 b:
5 b:
6 b:
7 b:
1 Central Processing Unit
8 b:
9 b:
X41 recommends to use the max parameter of strbuf_split_buf() to enforce sane boundaries to
the amount of items the function processes.
Severity: HIGH
CWE: 125 – Out-of-bounds Read
Affected Component: pretty.c:parse_padding_placeholder()
4.1.2.1 Description
An out-of-bounds read vulnerability was found during inspection and manual testing of the Git
code base.
When using an incomplete padding placeholder format string via the pretty printing of logs or
enabled export-subst configuration via the attributes, an out-of-bounds read can be triggered
as shown in the following listing:
It was found that leaking of memory contents is possible via git log and also via git archive using
the export-subst functionality:
The issue occurs in function parse_padding_placeholder() where the format specifier for padding
placeholders is parsed. The function tries to find the end of the format specifier using the function
strcspn(start, ",)"), which will return the number of bytes in the initial segment that does not match
the substring argument.
In this case, this will be all the bytes in the format string specifier which will result in the pointer
variable end to point to the end of the format string buffer:
The code tries to check if the end of the string (which will be a NUL byte) has been reached, but
does not dereference the returned pointer value and checks the pointer value itself to be not a
NULL pointer as seen in the following listing:
This will lead to an out-of-bounds read later in function strbuf_expand() invoked from the calling
function repo_format_commit_message() as can be debugged using gdb:
It is recommended to dereference the returned pointer value from strcspn() and check if it is a
NUL byte, which would indicate the end of the string given as first argument.
The following patch will fix the issue by dereferencing the end pointer before the comparison:
Listing 4.8: Patch to Check Dereferenced Character Value Instead of the Pointer Itself
Severity: LOW
CWE: 680 – Integer Overflow to Buffer Overflow
Affected Component: notes.c:combine_notes_concatenate()
4.1.3.1 Description
On 64-bit Microsoft Windows systems, the size of an unsigned long is 4 bytes2 while the size
of size_t is 8 bytes.
In combine_notes_concatenate() (see listing 4.9) two note objects are read and their sizes stored
in variables of type unsigned long. These are then used to calculate buf_len which will over-
flow when the input numbers are big enough. The buffer allocated for buf will be smaller than
expected and the call to memcpy() will write out of bounds.
This allows for an out-of-bounds heap write where the length of the allocation and overwrite can
be attacker controlled as well as the data written:
2 https://learn.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-170
To trigger the issue, the merge strategy needs to be set in .gitconfig (see listing 4.10):
1 [notes]
2 mergeStrategy = union
To create the actual merge, notes need to be added to commits and pushed to the repository. A
fetch afterwards will cause a conflict that needs to be resolved with git-merge:
24
25 echo -e "\n>> Second note\n"
26 cd ../32b-co2
27 git notes add -F /c/Users/eric/Desktop/test/1200B
28 git fetch origin refs/notes/commits:refs/notes/origin/commits
29
30 echo -e "\n>> Merge\n"
31 git notes merge -v origin/commits
Since this issue triggers only on Windows systems and the use of notes with the union merge
strategy does not seem common, this issue is rated low. Similar issues can be identified with
weggli '_ = read_object_file(_, _, &$len); _ + $len;' /code/git-2.38.1/, most only
add 1 to the size. Another issue with the same root cause is described in issue 4.2.9.
X41 recommends to convert the type of all variables that hold length or size values to size_t.
Furthermore, usage of the strbuf interface to concatenate strings may help to remedy similar
issues.
Severity: LOW
CWE: 1088 – Synchronous Access of Remote Resource without Timeout
Affected Component: wrapper.c
4.1.4.1 Description
When accessing remote resources while performing a git-clone no timeouts are in place. A
git-clone process can be kept alive for an arbitrary time by the remote end by simply not an-
swering with any data and keeping the TCP3 socket open.
This might allow DoS4 attacks on services where an attacker can ask Git processes to connect
to external services. The resource consumption issue of this is amplified by the fact that Git
spawns several sub processes for a git-clone, where the second process just translates from
git remote-http to git-remote-http :
On the attacker side the amount of resources required is minimal, after the initial TCP handshake
only the socket needs to be kept alive, which can be implemented efficiently using raw sockets.
X41 recommends to add timeouts to all read operations and set sane default values.
Severity: MEDIUM
CWE: 1333 – Inefficient Regular Expression Complexity
Affected Component: object-name.c:get_oid_oneline()
4.1.5.1 Description
This value is then passed on to regcomp() which interprets that data as regular expression and
compiles it as seen in the following stack trace:
This allows attackers to supply malicious regular expressions that might exhaust the stack of the
git process.
Besides this, ReDoS5 attacks are possible as well as shown in the following listing:
1 AddressSanitizer:DEADLYSIGNAL
2 =================================================================
3 ==3616490==ERROR: AddressSanitizer: stack-overflow on address 0x7ffc270b6f58 (pc 0x7fa1be2c043e
,→ bp 0x000000002d69 sp 0x7ffc270b6f50 T0)
4 #0 0x7fa1be2c043e in parse_expression posix/regcomp.c:2249
5 #1 0x7fa1be2c241c in parse_branch posix/regcomp.c:2207
6 #2 0x7fa1be2c26ea in parse_reg_exp posix/regcomp.c:2159
7 #3 0x7fa1be2c1190 in parse_sub_exp posix/regcomp.c:2496
8 #4 0x7fa1be2c1190 in parse_expression posix/regcomp.c:2282
9 #5 0x7fa1be2c241c in parse_branch posix/regcomp.c:2207
10 #6 0x7fa1be2c26ea in parse_reg_exp posix/regcomp.c:2159
11 #7 0x7fa1be2c1190 in parse_sub_exp posix/regcomp.c:2496
12 #8 0x7fa1be2c1190 in parse_expression posix/regcomp.c:2282
13 #9 0x7fa1be2c241c in parse_branch posix/regcomp.c:2207
14 #10 0x7fa1be2c26ea in parse_reg_exp posix/regcomp.c:2159
15 #11 0x7fa1be2c1190 in parse_sub_exp posix/regcomp.c:2496
16 #12 0x7fa1be2c1190 in parse_expression posix/regcomp.c:2282
17 ...
X41 recommends to not use attacker controlled data for regular expressions or sanitize it before-
hand.
5 https://en.wikipedia.org/wiki/ReDoS
Severity: CRITICAL
CWE: 122 – Heap-based Buffer Overflow
Affected Component: pretty.c:format_and_pad_commit()
4.1.6.1 Description
1 } else {
2 int sb_len = sb->len, offset = 0;
3 if (c->flush_type == flush_left)
4 offset = padding - len;
5 else if (c->flush_type == flush_both)
6 offset = (padding - len) / 2;
7 /*
8 * we calculate padding in columns, now
9 * convert it back to chars
10 */
11 padding = padding - len + local_sb.len;
12 strbuf_addchars(sb, ' ', padding);
13 memcpy(sb->buf + sb_len + offset, local_sb.buf,
14 local_sb.len);
15 }
The above code is reached when a padding specifier is used in the pretty format6 . local_sb is
a stringbuffer that points to the expanded format which is to be padded. It is possible to specify
a width of the padding up to (231 ) − 1, this is being limited in pretty.c line 1128 onward. Due
to sb_len and offset being of type int, an integer overflow can let the offset calculation on
sb->buf, sb_len + offset in the call to memcpy() overflow as well and result in a negative offset
against sb->buf. The following pretty format illustrates this on a Git executable compiled with
ASan7 .
51 0x0ff15d7360b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
52 0x0ff15d7360c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
53 0x0ff15d7360d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
54 0x0ff15d7360e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
55 =>0x0ff15d7360f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa[fa]
56 0x0ff15d736100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
57 0x0ff15d736110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
58 0x0ff15d736120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
59 0x0ff15d736130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
60 0x0ff15d736140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
61 Shadow byte legend (one shadow byte represents 8 application bytes):
62 Addressable: 00
63 Partially addressable: 01 02 03 04 05 06 07
64 Heap left redzone: fa
65 Freed heap region: fd
66 Stack left redzone: f1
67 Stack mid redzone: f2
68 Stack right redzone: f3
69 Stack after return: f5
70 Stack use after scope: f8
71 Global redzone: f9
72 Global init order: f6
73 Poisoned by user: f7
74 Container overflow: fc
75 Array cookie: ac
76 Intra object redzone: bb
77 ASan internal: fe
78 Left alloca redzone: ca
79 Right alloca redzone: cb
80 ==188760==ABORTING
The pretty format can also be used in git archive operations via the export-subst attribute.
The out-of-bounds write allows to write the string defined by the format specifier following the
second, overflowing padding specifier to a controlled offset before sb->buf.
This issue along with a proposed patch was, due to criticality, disclosed early on November 10th
to the Git security mailing list.
During the disclosure process a number of related overflows have been identified and patched
by Patrick Steinhardt and a security release is pending for mid-December 2022 at the time of
writing this report.
Severity: CRITICAL
CWE: 122 – Heap-based Buffer Overflow
Affected Component: attr.c:parse_attr_line()
A critical out-of-bounds heap issue was identified that can be triggered via a git clone or git
pull from a remote repository via SSH on untrustworthy infrastructure.
When parsing a line from .gitattributes, the following code in attr.c can overflow the counter
keeping check of the number of attributes that were parsed and are valid:
35 if (!attr_name_valid(name, namelen)) {
36 report_invalid_attr(name, namelen, src, lineno);
37 goto fail_return;
38 }
39 }
40 else
41 is_macro = 0;
42
43 states += strspn(states, blank);
44
45 /* First pass to count the attr_states */
46 for (cp = states, num_attr = 0; *cp; num_attr++) {
47 cp = parse_attr(src, lineno, cp, NULL);
48 if (!cp)
49 goto fail_return;
50 }
Later on the value of num_attr is used to allocate space on the heap that attribute data is then
written to as shown in the following listing 4.19:
1 res = xcalloc(1,
2 sizeof(*res) +
3 sizeof(struct attr_state) * num_attr +
4 (is_macro ? 0 : namelen + 1));
5 if (is_macro) {
6 res->u.attr = git_attr_internal(name, namelen);
7 } else {
8 char *p = (char *)&(res->state[num_attr]);
9 memcpy(p, name, namelen);
10 res->u.pat.pattern = p;
Due to variable num_attr being of type int (signed 32-bit wide), a very long attribute line or
many attribute lines can overflow the variable, causing the value to become negative.
A PoC8 to create a malicious .gitattributes file and commit it to a malicious repository is the
following:
1 perl -e 'print "A " . "\rh="x2000000000; print "\rh="x2000000000; print "\rh="x294967294 . "\n"'
,→ > .gitattributes
8 Proof of Concept
When cloning or pulling from the repository, a heap overflow occurs since the num_attrs value
will become negative (-2) and cause the space allocated via xcalloc() to be only 2 bytes large. A
subsequent write (res->u.pat.pattern = p ) will then write out of bounds to the heap:
Since the size of the truncated allocation and also the data written out-of-bounds seem to be
untrustworthy attacker controlled data from a remote repository, this is regarded as a critical
issue.
Furthermore, an abort() can be triggered via function report_invalid_attr() when a too large invalid
attribute name is parsed such as created by the following command:
After the file is committed, the abort() can be triggered via a checkout, reachable by at least
git-archive and git-pull:
1 $ git pull
2 From /home/user/f/../fuzzdit
3 058b4fb..9f0a05d master -> origin/master
4 Updating 058b4fb..9f0a05d
5 BUG: strbuf.c:400: your vsnprintf is broken (returned -1)
6 error: merge died of signal 6
During the disclosure process a number of related overflows have been identified and patched
by Patrick Steinhardt and a security release is pending for mid-December 2022 at the time of
writing this report.
X41 recommends to limit the number and lengths of attributes parsed. Furthermore, the code
using signed integer types for length values should be refactored to avoid signed types and use
64-bit unsigned types instead.
Severity: LOW
CWE: 400 – Uncontrolled Resource Consumption (’Resource Exhaustion’)
Affected Component: apply.c:parse_chunk()
4.1.8.1 Description
When a maliciously crafted patch is parsed via parse_chunk() it is accepted as valid, but the size
returned as 0. In case a patch cannot be parsed by parse_single_patch(), the code checks whether
it is a line that identifies differing binary files. If that line is long enough the addition offset
+ hdrsize + patchsize can become big enough to overflow the return value of type int. By
crafting the header and line in the right way, the addition will overflow to 0 :
1 static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch
,→ *patch)
2 {
3 int hdrsize, patchsize;
4 int offset = find_header(state, buffer, size, &hdrsize, patch);
5
6 if (offset < 0)
7 return offset;
8 ...
9 if (!patchsize) {
10 static const char git_binary[] = "GIT binary patch\n";
11 int hd = hdrsize + offset;
12 unsigned long llen = linelen(buffer + hd, size - hd);
13 ...
14 else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
15 static const char *binhdr[] = {
16 "Binary files ",
17 "Files ",
18 NULL,
19 };
20 int i;
21 for (i = 0; binhdr[i]; i++) {
22 int len = strlen(binhdr[i]);
23 if (len < size - hd &&
24 !memcmp(binhdr[i], buffer + hd, len)) {
25 state->linenr++;
26 patch->is_binary = 1;
27 patchsize = llen;
28 break;
29 }
30 }
31 }
32
33 /* Empty patch cannot be applied if it is a text patch
34 * without metadata change. A binary patch appears
35 * empty to us here.
36 */
37 if ((state->apply || state->check) &&
38 (!patch->is_binary && !metadata_changes(patch))) {
39 error(_("patch with only garbage at line %d"), state->linenr);
40 return -128;
41 }
42 }
43
44 return offset + hdrsize + patchsize;
45 }
A file that can trigger the DoS when applied via git-apply can be created easily:
1 #/usr/bin/ruby
2
3 f = File.open("poc.patch","w")
4 f.write 'diff --git a/b b/b
5 index 61c6dc5..9570850 100644
6 --- a/b
7 +++ b/b
8 Binary files b and '
9
10
11 # 28 for rest of the line, 65 for header before it
12 0.upto 4294967296 - 28 - 65 do
13 f.write 'b'
14 end
15
16 f.write ' differ
17 '
When the testcase is run on an ASan compiled binary, an out-of-bounds read in parse_git_diff_header()
will stop the execution early, but it runs fine without ASan.
X41 recommends to test the return value of parse_chunk() for 0 as well and change the size
variables to size_t type.
The following observations do not have a direct security impact, but are related to security hard-
ening, affect functionality, or other topics that are not directly related to security. X41 recom-
mends to mitigate these issues as well, because they often become exploitable in the future.
Doing so will strengthen the security of the system and is recommended for defense in depth.
4.2.1.1 Description
In bisect_skipped_commits() a file is opened using fopen() and the file handle stored in fp.
In case the write with fprintf() fails that file handle is not released as shown in the following listing
4.26:
The impact of this is minimal, since all code paths leading to this function seem to lead to an exit()
in case of errors where most modern operating systems will close that file handle.
Please note that Git relies quite heavy on the operating system for cleanup, especially in error
cases when die() or BUG() is called. These cases will not be listed in this report.
X41 recommends to close the file handle using fclose() during the error handling.
4.2.2.1 Description
In cap_object_info() the variable info is not initialized and only filled with data when packet_reader_read()
receives a request->line of size.
This leads to the use of an uninitialized info variable as parameter to send_info(), which then
sends data based on its setting via packet_writer_write() to a remote repository.
Since info.size is a single bit bit field, this discloses the value of a single bit of stack data:
Since this discloses only a single bit of data, the security impact is considered minimal and this is
only an informational finding.
4.2.3.1 Description
The directory compat contains several third party components of which some are outdated. Since
this folder is not part of git core, this is considered out of scope for this audit and therefore
informational.
compat/zlib-uncompress2.c contains code parts from zlib9 1.2.11, whereas the newest version
is 1.2.13, but imported the code is not exposing the code vulnerable to CVE-2022-3743410 .
Other parts, such as compat/regexp/, obstack.c and compat/poll/, seem to have been im-
ported from the GNU libc13 and modified.
X41 recommends to properly document where to find the upstream versions of the various files
and to implement a procedure that helps with tracking upstream updates and importing them.
9 https://zlib.net/
10 https://nvd.nist.gov/vuln/detail/CVE-2022-37434
11 https://www.nedprod.com/programs/portable/nedmalloc/
12 https://github.com/ned14/nedmalloc
13 https://www.gnu.org/software/libc/
4.2.4.1 Description
The hash used by the hashmap implementation in hashmap.c is FNV-114 , which is not collision
resistant and for which zero hashes and collisions have been identified15 .
This allows attackers to degrade the hashmap implementation into a linked list16 , which degrades
performance and might lead to DoS situations.
The hash tables are used for branches, configuration settings, objects and other values that at-
tackers might be able to influence.
X41 recommends to use a keyed hash function such as SipHash17 and key it with a randomly
generated value.
14 https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1_hash
15 http://isthe.com/chongo/tech/comp/fnv/#zero-hash
16 https://www.aumasson.jp/siphash/siphashdos_29c3_slides.pdf
17 https://en.wikipedia.org/wiki/SipHash
4.2.5.1 Description
Git is able to manage and cache credentials for the user via configuration files or helper tools. This
functionality uses struct credential to keep track of each credential along with the username
and password. When a credential is removed from the cache, the allocated memory gets freed,
but the contents of the memory are not wiped.
This causes the username and password to remain in the memory of the running process. Pass-
words in memory could be retrieved by an attacker with local access or via an information leak.
X41 recommends to wipe the memory with memset_explicit(), memset_s() or explicit_bzero(), which
guarantee that the compiler does not optimize them out.
4.2.6.1 Description
The Git credential caching daemon uses a directory for the creation of sockets to communicate
with other processes. Since access to these sockets might allow attackers to gain access to the
credentials, the daemon tries to ensure that the directory containing them can only be accessed
by the current user. This check happens before the daemon calls chdir() to change into the direc-
tory. In the case that an attacker has access to the parent directory, the attacker would be able
to delete and recreate the directory with less restrictive permissions.
Since this scenario seems not likely, this is considered an informational note instead of a finding.
32 }
X41 recommends to check the directory permissions after the call to chdir() as well as an addi-
tional hardening measure.
4.2.7.1 Description
MIDX18 file parsing is subject to OOB19 accesses, which can be easily identified by fuzzing.
A simple fuzzing run with AFL++ on the git-multi-pack-index verify command identified
several (see listing 4.30 and 4.31) crashes.
These were not investigated further, as multi-pack-index files do not seem to be attacker con-
trolled. The corresondping ASan output is given in the following listings:
28 0x0fe64cfc4050: fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe fe
29 Shadow byte legend (one shadow byte represents 8 application bytes):
30 Addressable: 00
31 Partially addressable: 01 02 03 04 05 06 07
32 Heap left redzone: fa
33 Freed heap region: fd
34 Stack left redzone: f1
35 Stack mid redzone: f2
36 Stack right redzone: f3
37 Stack after return: f5
38 Stack use after scope: f8
39 Global redzone: f9
40 Global init order: f6
41 Poisoned by user: f7
42 Container overflow: fc
43 Array cookie: ac
44 Intra object redzone: bb
45 ASan internal: fe
46 Left alloca redzone: ca
47 Right alloca redzone: cb
48 Shadow gap: cc
49 ==644026==ABORTING
1 AddressSanitizer:DEADLYSIGNAL
2 =================================================================
3 ==644017==ERROR: AddressSanitizer: SEGV on unknown address 0x7fd76780b4cc (pc 0x7fd26a53db24 bp
,→ 0x7ffcd3f0c390 sp 0x7ffcd3f0bb28 T0)
4 ==644017==The signal is caused by a READ memory access.
5 #0 0x7fd26a53db24 string/../sysdeps/x86_64/multiarch/memcmp-avx2-movbe.S:270
6 #1 0x43777e in MemcmpInterceptorCommon(void*, int (*)(void const*, void const*, unsigned
,→ long), void const*, void const*, unsigned long)
,→ (/home/eric/code/git-2.38.1-midx/git-multi-pack-index+0x43777e)
7 #2 0x437afa in memcmp (/home/eric/code/git-2.38.1-midx/git-multi-pack-index+0x437afa)
8 #3 0xe4080e in hashcmp_algop /home/eric/code/git-2.38.1-midx/./hash.h:212:9
9 #4 0xe4080e in hashcmp /home/eric/code/git-2.38.1-midx/./hash.h:217:9
10 #5 0xe4080e in bsearch_hash /home/eric/code/git-2.38.1-midx/hash-lookup.c:113:13
11 #6 0xf5ca13 in bsearch_midx /home/eric/code/git-2.38.1-midx/midx.c:241:9
12 #7 0xf5ca13 in fill_midx_entry /home/eric/code/git-2.38.1-midx/midx.c:290:7
13 #8 0xf6d784 in verify_midx_file /home/eric/code/git-2.38.1-midx/midx.c:1777:8
14 #9 0x7adde9 in cmd_multi_pack_index
,→ /home/eric/code/git-2.38.1-midx/builtin/multi-pack-index.c:282:8
15 #10 0x4d6946 in run_builtin /home/eric/code/git-2.38.1-midx/git.c:466:11
16 #11 0x4d021f in handle_builtin /home/eric/code/git-2.38.1-midx/git.c:721:3
17 #12 0x4cfa62 in cmd_main /home/eric/code/git-2.38.1-midx/git.c:889:3
18 #13 0x9facea in main /home/eric/code/git-2.38.1-midx/common-main.c:56:11
19 #14 0x7fd26a406d09 in __libc_start_main csu/../csu/libc-start.c:308:16
X41 recommends to identify the root cause of these issues and to continue the fuzz testing of
MIDX file processing.
4.2.8.1 Description
The command git-bundle can bundle a repository into a file. When the create option is called
but the filename parameter is missing the command crashes due to a NULL pointer dereference
as shown in the following ASan trace:
1 $ ~/code/git-2.38.1/git-bundle create
2 AddressSanitizer:DEADLYSIGNAL
3 =================================================================
4 ==180509==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f48935f1d76 bp
,→ 0x7ffdd3aab280 sp 0x7ffdd3aaaa28 T0)
5 ==180509==The signal is caused by a READ memory access.
6 ==180509==Hint: address points to the zero page.
7 #0 0x7f48935f1d76 (/lib/x86_64-linux-gnu/libc.so.6+0x9ad76)
8 #1 0x7f48937a7a8c in __interceptor_strlen
,→ ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:368
9 #2 0x555b41cd892d in strbuf_addstr /home/eric/code/git-2.38.1/strbuf.h:305
10 #3 0x555b41cd892d in prefix_filename /home/eric/code/git-2.38.1/abspath.c:277
11 #4 0x555b41afed11 in parse_options_cmd_bundle builtin/bundle.c:53
12 #5 0x555b41aff7dc in cmd_bundle_create builtin/bundle.c:79
13 #6 0x555b41b00d14 in cmd_bundle builtin/bundle.c:212
14 #7 0x555b41ac9641 in run_builtin /home/eric/code/git-2.38.1/git.c:466
15 #8 0x555b41ac9d8d in handle_builtin /home/eric/code/git-2.38.1/git.c:721
16 #9 0x555b41acd903 in cmd_main /home/eric/code/git-2.38.1/git.c:889
17 #10 0x555b41cd6d52 in main /home/eric/code/git-2.38.1/common-main.c:56
18 #11 0x7f489357ad09 in __libc_start_main ../csu/libc-start.c:308
19 #12 0x555b41ac9099 in _start (/home/eric/code/git-2.38.1/git-bundle+0x1ca099)
20
21 AddressSanitizer can not provide additional info.
22 SUMMARY: AddressSanitizer: SEGV (/lib/x86_64-linux-gnu/libc.so.6+0x9ad76)
23 ==180509==ABORTING
X41 recommends to check for the missing parameter to avoid the NULL pointer dereference and
display an error for a better user experience.
4.2.9.1 Description
On 64 bit Microsoft Windows systems, the size of an unsigned long is 4 bytes20 while the size
of size_t is 8 bytes. This can lead to security issues since the git code mixes the use of both
types, assuming they are of the same size.
One example can be seen in listing 4.33 which shows fake_working_tree_commit(). The variable
buf_len is an unsigned long and passed to strbuf_attach() which expects a size_t. The value
is passed twice, in the latter case after being increased by 1. If buf_len is 4294967295 (232 − 1)
the calculation will overflow to 0.
This example can be triggered by a textconv helper21 returning this exact amount of bytes when
called by git blame. The affected code is shown in the following listing:
1 char *buf_ptr;
2 unsigned long buf_len;
3
4 if (contents_from) {
5 if (stat(contents_from, &st) < 0)
6 die_errno("Cannot stat '%s'", contents_from);
7 read_from = contents_from;
8 }
9 else {
10 if (lstat(path, &st) < 0)
11 die_errno("Cannot lstat '%s'", path);
12 read_from = path;
13 }
14 mode = canon_mode(st.st_mode);
15
16 switch (st.st_mode & S_IFMT) {
17 case S_IFREG:
18 if (opt->flags.allow_textconv &&
19 textconv_object(r, read_from, mode, null_oid(), 0, &buf_ptr, &buf_len))
20 strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
20 https://learn.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-170
21 https://git.wiki.kernel.org/index.php/Textconv
The example given does not have a security impact but is used to highlight the issue at hand.
Microsoft Windows was not a primary target of the audit, therefore not all instances where this
could cause issues have been investigated.
Variations of this issue can be found with the weggli22 command weggli 'unsigned long $a;
strbuf_attach(_, _, $a, $a + _);' git-2.38.1 or by inspecting the compiler warnings when
compiling for Microsoft Windows 64 bit machines.
X41 suggests to convert all instances where length or size values are processed to size_t.
22 https://github.com/googleprojectzero/weggli
4.2.10.1 Description
Git uses variables of types unsigned long and int throughout the code base for variables that
track sizes or lengths. Since int is only 4 byte wide on Linux and Microsoft Windows 64-bit
systems and unsigned long is 4 byte wide on 64-bit Microsoft Windows systems, this can cause
integer truncation or overflow issues. In various places, size_t variables are properly used to
track sizes but these are then cast into one of the variable types with a smaller bit-width.
During this audit, this was the root cause for several issues identified.
The usage of parse_chunk() provides a good example on how various variable types are mixed
to handle sizes. apply_patch() supplies a size_t variable, where the function expects unsigned
long, which leads to truncation on 64-bit Windows.
parse_chunk() returns an offset into the original buffer as an int type, which further truncates
the length and might even become negative.
1 /*
2 * Read the patch text in "buffer" that extends for "size" bytes; stop
3 * reading after seeing a single patch (i.e. changes to a single file).
4 * Create fragments (i.e. patch hunks) and hang them to the given patch.
5 *
6 * Returns:
7 * -1 if no header was found or parse_binary() failed,
8 * -128 on another error,
9 * the number of bytes consumed otherwise,
10 * so that the caller can call us again for the next patch.
11 */
12 static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch
,→ *patch)
13 ...
14
15 static int apply_patch(struct apply_state *state,
16 int fd,
17 const char *filename,
18 int options)
19 {
20 size_t offset;
21 ...
22 while (offset < buf.len) {
23 struct patch *patch;
24 int nr;
25 ...
26 nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch);
Due to time constraints not all instances could be investigated where integer truncation occurs.
Compiling with "-Wsign-compare -Wsign-conversion -Wconversion" or running one of the
various analyzers generates too many warnings to be audited in the time given. Infer reports
around 1600 integer overflows, the Visual Studio analyzer around 2500 related issues and build-
ing using gcc with related warnings enabled (see above) results in nearly 18000 warnings. Espe-
cially on 64-bit Windows systems this is a cause for concern.
X41 recommends to refactor the code base and replace all size and length variables with size_t
typed ones. Additionally, X41 recommends to build the code base with the "-Wsign-compare
-Wsign-conversion -Wconversion" compiler parameters to catch similar errors.
4.2.11.1 Description
Since the code uses client_sid instead of sid, the string is not NUL-terminated and might
contain further data sent by the client as shown here:
X41 recommends to change the variable in the call to trace2_data_string() from client_sid to
sid.
4.2.12.1 Description
Git allows to sign pushes with either GPG23 or SSH keys. For this a NONCE24 is generated by the
server, which is signed by the client along with the push. The NONCE is generated by the concate-
nation of a timestamp and the HMAC25 -SHA-126 of HMAC-SHA-25627 of that timestamp and a
secret called cert_nonce_seed. This secret can be configured by setting receive.certnonceseed
in the git configuration.
No length checks or other sanity checks are performed on this seed. An attacker might therefore
try to brute-force attack the seed value to be able to generate NONCE values without interacting
with the server.
Cracking speeds of 5200.0 MH/s for HMAC-SHA-1 and 1898.6 MH/s for HMAC-SHA-256 have
been reported28 on a single GPU29 .
Since not only the NONCE is signed but the whole push, no clear attack vector is given this issue is
considered informational. More information about the threat model of the NONCE can be found
in the commit30 that introduced a constant time memcmp() function for NONCE verification.
4.2.13.1 Description
The NONCE used for the verification of git push signatures is verified by calculating an HMAC
over a timestamp which is seeded by a secret (see listing 4.2.12 as well).
When performing an HMAC operation, the key (or a hash of it) is expanded into two pads that
are then passed into the used hashing function before the input data is added as well.
The call in prepare_push_cert_nonce() mixes the parameters to hmac_hash() and supplies the input
text as key. Since this would be known to an attacker that tries to brute force the secret, the brute
forcing could be significantly optimized.
31 the_hash_algo->final_fn(out, &ctx);
32
33 /* RFC 2104 2. (6) & (7) */
34 the_hash_algo->init_fn(&ctx);
35 the_hash_algo->update_fn(&ctx, k_opad, sizeof(k_opad));
36 the_hash_algo->update_fn(&ctx, out, the_hash_algo->rawsz);
37 the_hash_algo->final_fn(out, &ctx);
38 }
39
40 static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
41 {
42 struct strbuf buf = STRBUF_INIT;
43 unsigned char hash[GIT_MAX_RAWSZ];
44
45 strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
46 hmac_hash(hash, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));
47 strbuf_release(&buf);
4.2.14.1 Description
The NONCE used to verify signed pushes31 is not stored server-side and could in theory be
replayed by a MITM32 attack.
This replay can only happen during the time the timestamp of the NONCE is valid. Some imple-
mentations seem to set it to 5 minutes33 .
Additionally, since the NONCE is based on seconds, multiple client connections might receive
the same NONCE.
Since the push itself is part of the signed buffer, a replay attack does not seem to have a security
impact. Since no clear attack vector is given and this issue is considered informational.
X41 recommends to document the threat model the signed pushes try to protect against with
the use of a NONCE.
31 https://git-scm.com/docs/git-push
32 Man-in-the-middle Attack
33 https://gerrit-documentation.storage.googleapis.com/Documentation/2.12/config-gerrit.html
4.2.15.1 Description
This could result in out-of-band access and, possibly crashes. The affected code is shown in the
following listing:
Since the process is short lived and no memory seems to get exfiltrated no clear security impact
is discernible and this issue is considered informational.
4.2.16.1 Description
The NONCE in use for push signatures is based on a timestamp of seconds since 1970-01-01
00:00:00.
This would allow a client to sanity check the NONCE and make sure its not based in the future
or already expired. The affected code is shown in the following listing:
In case an attacker was able to brute force the seed used by a certificate, that attacker might
perform a MITM attack on another server and send a NONCE that is based in the future to be
able to replay the push to the first server later.
X41 recommends to perform sanity checking of the time value of the NONCE.
4.2.17.1 Description
Git implements temporary file creation and handling in tempfile.c, which ensures that tempo-
rary files are deleted in case of errors by installing signal handlers. Nevertheless, various other
temporary file helper routines exist, such as odb_mkstemp(), xmkstemp(), git_mkstemp_mode() and
others, which do not remove the temporary files on errors.
Due to the fact that Git usually relies on the operating system for cleanup in case of errors when
die() or BUG() is called, this can lead to an accumulation of temporary files when repeated errors
are triggered.
This causes issues when fuzz testing various Git binaries, but could also be abused by attackers
to generate DoS situations by having a server repeatedly process invalid data until that system
runs out of inodes or disk space.
X41 recommends to unify all temporary file handling by always using the tempfile.c implemen-
tation and to ensure that temporary files are always deleted in error cases.
4.2.18.1 Description
X41 recommends to use xmalloc() instead or check the temp variable against NULL.
4.2.19.1 Description
The functions get_oid_1() and get_nth_ancestor() call each other to parse the variable name. In
case of a long string, this might lead to a stack overflow:
1 AddressSanitizer:DEADLYSIGNAL
2 =================================================================
3 ==2924178==ERROR: AddressSanitizer: stack-overflow on address 0x7ffe7ee73f00 (pc 0x55a64816b0b5
,→ bp 0x7ffe7ee74270 sp 0x7ffe7ee73f00 T0)
4 #0 0x55a64816b0b5 in get_oid_1 /home/eric/code/git-2.38.1/object-name.c:1231
5 #1 0x55a64816b479 in get_nth_ancestor /home/eric/code/git-2.38.1/object-name.c:1065:8
6 #2 0x55a64816b479 in get_oid_1 /home/eric/code/git-2.38.1/object-name.c:1268:10
7 #3 0x55a64816b479 in get_nth_ancestor /home/eric/code/git-2.38.1/object-name.c:1065:8
8 #4 0x55a64816b479 in get_oid_1 /home/eric/code/git-2.38.1/object-name.c:1268:10
9 #5 0x55a64816b479 in get_nth_ancestor /home/eric/code/git-2.38.1/object-name.c:1065:8
10 #6 0x55a64816b479 in get_oid_1 /home/eric/code/git-2.38.1/object-name.c:1268:10
11 #7 0x55a64816b479 in get_nth_ancestor /home/eric/code/git-2.38.1/object-name.c:1065:8
12 #8 0x55a64816b479 in get_oid_1 /home/eric/code/git-2.38.1/object-name.c:1268:10
13 ...
This can be triggered via git-fast-import and will result in the following function call stack as
visible using gdb:
1 >>> bt
2 #1 0x0000555555b147a0 in get_oid_with_context_1 (repo=0x5555560fcfa0 <the_repo>, name=<optimized
,→ out>, flags=<optimized out>, prefix=<optimized out>, oid=<optimized out>, oc=<optimized out>)
,→ at object-name.c:1919
3 #2 0x0000555555b1604f in get_oid_with_context (repo=<optimized out>, str=<optimized out>,
,→ flags=<optimized out>, oid=<optimized out>, oc=<optimized out>) at object-name.c:2068
4 #3 0x0000555555b16106 in repo_get_oid (r=0x5555560fcfa0 <the_repo>,
,→ name=name@entry=0x62d00001442b "@", '~' <repeats 199 times>..., oid=oid@entry=0x7fffffffdbb0)
,→ at object-name.c:1705
5 #4 0x00005555557c3211 in note_change_n (p=p@entry=0x62d000014402 '0' <repeats 40 times>, " @",
,→ '~' <repeats 158 times>..., b=b@entry=0x7ffff3a1b180,
,→ old_fanout=old_fanout@entry=0x7fffffffdd30 "") at builtin/fast-import.c:2489
6 #5 0x00005555557c3f00 in parse_new_commit (arg=<optimized out>) at builtin/fast-import.c:2736
An example input is shown in the next listing where the recursion happens for each tilde (˜) at
the end of the data:
1 blob
2 mark :
3 data
4 commit 0
5 mark :
6 committer <> 0 +0
7 data
8 N 0000000000000000000000000000000000000000 @~~~~~~~~~~~~~~~
X41 recommends to limit the recursion depth to avoid crashes during parsing.
4.2.20.1 Description
An invalid read is caused when parse_reset_branch() is called on an invalid branch state, which
can be triggered via git-fast-import, resulting in the following ASan trace:
1 AddressSanitizer:DEADLYSIGNAL
2 =================================================================
3 ==2223720==ERROR: AddressSanitizer: SEGV on unknown address 0x00009fff8001 (pc 0x556d44460b10 bp
,→ 0x7fbccfd1b3e8 sp 0x7ffe62f06b90 T0)
4 ==2223720==The signal is caused by a READ memory access.
5 #0 0x556d44460b10 in release_tree_entry builtin/fast-import.c:718
6 #1 0x556d44460b92 in release_tree_content_recursive builtin/fast-import.c:679
7 #2 0x556d44460b25 in release_tree_entry builtin/fast-import.c:719
8 #3 0x556d44460b92 in release_tree_content_recursive builtin/fast-import.c:679
9 #4 0x556d4446f004 in parse_reset_branch builtin/fast-import.c:2887
10 #5 0x556d444779c8 in cmd_fast_import builtin/fast-import.c:3572
11 #6 0x556d443d1641 in run_builtin /home/eric/code/git-2.38.1/git.c:466
12 #7 0x556d443d1d8d in handle_builtin /home/eric/code/git-2.38.1/git.c:721
13 #8 0x556d443d5903 in cmd_main /home/eric/code/git-2.38.1/git.c:889
14 #9 0x556d445ded52 in main /home/eric/code/git-2.38.1/common-main.c:56
15 #10 0x7fbcd37e3d09 in __libc_start_main ../csu/libc-start.c:308
16 #11 0x556d443d1099 in _start (/home/eric/code/git-2.38.1/git-fast-import+0x1ca099)
17
18 AddressSanitizer can not provide additional info.
19 SUMMARY: AddressSanitizer: SEGV builtin/fast-import.c:718 in release_tree_entry
20 ==2223720==ABORTING
This issue can be triggered by importing the data shown via git-fast-import:
1 blob
2 data
3 commit r/heads/master
4 committer <> 0 +0
5 data
6 C 00000
7 commit 0
8 committer <> 0 +0
9 data 2
10 00C master
11 commit r/heads/master
12 committer <> 0 +0
13 data 2
14 00C 00000/master
15 commit 0
16 committer <> 0 +0
17 data 2
18 00R 0
19 reset r/heads/master
1 blob
2 mark :
3 data
4 commit re00/head0/ma0ter
5 author <> 0 +0
6 committer <> 0 +0
7 data
8 C eee00000000<00e0 re00
9 commit 0
10 author <> 0 +0
11 committer <> 0 +0
12 data 2
13 00C 0/head0
14 commit re00/head0/ma0ter
15 mark :
16 committer <> 0 +0
17 data 2
18 00C eee00000000<00e0 re00/head0/0
Since the location of the read does not seem to be attacker controlled, this is considered an
informational finding and not investigated further.
4.2.21.1 Description
Repositories can be shared locally by multiple users. git-init offers the shared parameter that
allows multiple users in the same Unix group to push and fetch from that repository. For this, it
is required to set the group ownership of the appropriate files to that of the shared group.
Several instructions on how to set this up this can be found on the Internet3435 . All of these
change the group ownership on all files and directories in the shared repository. This includes
the folder containing hooks, which can then be abused to make other users execute malicious
code on various Git actions.
X41 recommends to improve the documentation on shared local repositories. The updated doc-
umentation should specify which permissions can be securely set in a shared setup.
34 https://nozaki.me/roller/kyle/entry/creating-a-shared-git-repository
35 https://serverfault.com/a/694369
4.2.22.1 Description
When accessing Git repositories via SSH and an active git-shell, users are restricted to certain
commands. This prevents the user from accessing the full machine and only allows to interact
with Git. The parameter of the allowed Git commands (git-receive-pack, git-upload-pack
and git-upload-archive) specifies the repository to perform the command against. This repos-
itory can be specified as a path relative to the users home directory.
This allows remote users to enumerate directories on the server via paths that enter the directo-
ries the attacker wants to fingerprint An example is given in the following listing:
X41 recommends to strip dots and slashes from the git-upload-pack command and others be-
fore calling them.
4.2.23.1 Description
But cp could be used after new_path is freed in a call to reject_tree_in_index() (see listing 4.47)
as visible in the following code fragments:
This can be triggered by executing git-cat-file blob ":./test". But as shown in listing 4.48
the variable is only accessed in case only_to_die is set (which it is in our example) and test
is a sparse directory. Since it was not possible in the time given to reproduce this on a sparse
directory, this is considered an informational finding.
4.2.24.1 Description
In git_header_name() the header of a patch is parsed and the name and second strings are com-
pared. The parsing checks for a newline at the end of second and assumes it is always as long as
name.
When second is shorter than name the access at second[len] reads out-of-bounds as seen in
the following code listing:
1 /*
2 * Accept a name only if it shows up twice, exactly the same
3 * form.
4 */
5 second = strchr(name, '\n');
6 if (!second)
7 return NULL;
8 line_len = second - name;
9 for (len = 0 ; ; len++) {
10 switch (name[len]) {
11 default:
12 continue;
13 case '\n':
14 return NULL;
15 case '\t': case ' ':
16 /*
17 * Is this the separator between the preimage
18 * and the postimage pathname? Again, we are
19 * only interested in the case where there is
20 * no rename, as this is only to set def_name
21 * and a rename patch has the names elsewhere
22 * in an unambiguous form.
23 */
24 if (!name[len + 1])
25 return NULL; /* no postimage name */
26 second = skip_tree_prefix(p_value, name + len + 1,
27 line_len - (len + 1));
28 if (!second)
29 return NULL;
30 /*
31 * Does len bytes starting at "name" and "second"
32 * (that are separated by one HT or SP we just
33 * found) exactly match?
34 */
Since the output of the read does not seem to be reflected to attackers this is considered an
informational finding and not further investigated.
X41 recommends to add an additional length check for the size of second.
4.2.25.1 Description
In parse_git_diff_header() the header line length of a patch is parsed with linelen() which returns
an unsigned long.
The return value is cast to a signed int len and subsequently being used as an array index to
line causing an out-of-bounds read with negative array indices:
The same pattern can be found in parse_fragment() and a maliciously constructed patch file can
trigger the issue in that code path as well:
1 AddressSanitizer:DEADLYSIGNAL
2 =================================================================
3 ==3521177==ERROR: AddressSanitizer: SEGV on unknown address 0x7f2352a7084e (pc 0x55cba08f4ca7 bp
,→ 0x000080000005 sp 0x7ffdc084f160 T0)
4 ==3521177==The signal is caused by a READ memory access.
5 #0 0x55cba08f4ca7 in parse_fragment /home/eric/code/git-2.38.1/apply.c:1687
6 #1 0x55cba08fb40c in parse_single_patch /home/eric/code/git-2.38.1/apply.c:1792
7 #2 0x55cba0900f5f in parse_chunk /home/eric/code/git-2.38.1/apply.c:2133
8 #3 0x55cba0901d94 in apply_patch /home/eric/code/git-2.38.1/apply.c:4700
9 #4 0x55cba0902537 in apply_all_patches /home/eric/code/git-2.38.1/apply.c:4934
10 #5 0x55cba06e134b in cmd_apply builtin/apply.c:28
11 #6 0x55cba06c7641 in run_builtin /home/eric/code/git-2.38.1/git.c:466
12 #7 0x55cba06c7d8d in handle_builtin /home/eric/code/git-2.38.1/git.c:721
13 #8 0x55cba06cb903 in cmd_main /home/eric/code/git-2.38.1/git.c:889
14 #9 0x55cba08d4d52 in main /home/eric/code/git-2.38.1/common-main.c:56
15 #10 0x7f25bf62ad09 in __libc_start_main ../csu/libc-start.c:308
16 #11 0x55cba06c7099 in _start (/home/eric/code/git-2.38.1/git-apply+0x1ca099)
17
18 AddressSanitizer can not provide additional info.
19 SUMMARY: AddressSanitizer: SEGV /home/eric/code/git-2.38.1/apply.c:1687 in parse_fragment
20 ==3521177==ABORTING
Since the output of the read does not seem to be reflected to attackers this is considered an
informational finding and not further investigated.
X41 recommends to add an additional length check for negative values of len.
4.2.26.1 Description
The function ewah_read_mmap() reads values from a memory mapped buffer as seen in the fol-
lowing listing 4.52:
37 self->buffer[i] = ntohll(self->buffer[i]);
38
39 if (len < sizeof(uint32_t))
40 return error("corrupt ewah bitmap: eof before rlw");
41
42 // MARK unchecked offset to buffer read from external input,
43 // this could take the buffer pointer out-of-bounds
44 self->rlw = self->buffer + get_be32(ptr);
45 ptr += sizeof(uint32_t);
46 len -= sizeof(uint32_t);
47
48 return ptr - (const uint8_t *)map;
49 }
The value used to set the self->alloc_size used in REALLOC_ARRAY to allocate a memory
buffer on the heap is not restricted. Attackers could set the value to a large value and make the
allocation fail or set the value to zero. Setting it to zero would result in self->buffer to point to
a zero allocated heap chunk, which could potentially lead to problems in other parts of the code,
should they assume the buffer is not zero length.
When calculating an offset to store in self->rlw, the code reads a 32-bit big endian integer value
from the mapped memory, but fails to check if the resulting pointer is still within the bounds
of the self->buffer allocated memory. Should self->rlw be used later, the access will be
out-of-bounds and become a memory safety issue.
Since the output of the read does not seem to be reflected to remote attackers and a bitmap index
seems to be only parsed locally by git, we consider this an informational finding. Depending on
the context, the issue might be security relevant, but investigating this is outside the scope of
this review.
X41 recommends to validate both the values used to allocate heap memory and to check if the
pointer stored in self->rlw is within the bounds of self->buffer.
4.2.27.1 Description
The while loop increases the pointer in before calling itself again in case c is an opening bracket.
In case the entire string is just an opening bracket, in will now point at the terminating NUL-byte.
In the next iteration of the function, in will be increased again and the while loop aborted, since
c is 0. The function will now return a pointer that points behind the string itself.
1 from:(
1 =================================================================
2 ==1437178==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000002a58 at pc
,→ 0x000000d2ab12 bp 0x7ffc14d82750 sp 0x7ffc14d82748
3 READ of size 1 at 0x603000002a58 thread T0
4 #0 0xd2ab11 in unquote_quoted_pair /home/eric/code/git-2.38.1-fuzz-unpack/mailinfo.c:119:14
5 #1 0xd29be9 in handle_from /home/eric/code/git-2.38.1-fuzz-unpack/mailinfo.c:147:2
6 #2 0xd1cc82 in handle_info /home/eric/code/git-2.38.1-fuzz-unpack/mailinfo.c:1175:4
7 #3 0xd1a56e in mailinfo /home/eric/code/git-2.38.1-fuzz-unpack/mailinfo.c:1225:2
8 #4 0x724176 in cmd_mailinfo /home/eric/code/git-2.38.1-fuzz-unpack/builtin/mailinfo.c:108:13
9 #5 0x4d3a96 in run_builtin /home/eric/code/git-2.38.1-fuzz-unpack/git.c:466:11
10 #6 0x4cc839 in handle_builtin /home/eric/code/git-2.38.1-fuzz-unpack/git.c:721:3
11 #7 0x4cc070 in cmd_main /home/eric/code/git-2.38.1-fuzz-unpack/git.c:889:3
12 #8 0x967888 in main /home/eric/code/git-2.38.1-fuzz-unpack/common-main.c:56:11
13 #9 0x7fcf3173bd09 in __libc_start_main csu/../csu/libc-start.c:308:16
14 #10 0x420a39 in _start (/home/eric/code/git-2.38.1-fuzz-unpack/git-mailinfo+0x420a39)
15
16 0x603000002a58 is located 0 bytes to the right of 24-byte region [0x603000002a40,0x603000002a58)
17 allocated by thread T0 here:
18 #0 0x49aa29 in realloc (/home/eric/code/git-2.38.1-fuzz-unpack/git-mailinfo+0x49aa29)
19 #1 0x1241066 in xrealloc /home/eric/code/git-2.38.1-fuzz-unpack/wrapper.c:136:8
This read does not seem to have any security implications, therefore this is considered an infor-
mational finding.
36 https://clang.llvm.org/docs/AddressSanitizer.html
X41 recommends to add additional NUL -byte checks to the unquoting functions in mailinfo.c.
X41 D-Sec GmbH is an expert provider for application security and penetration testing services.
Having extensive industry experience and expertise in the area of information security, a strong
core security team of world-class security experts enables X41 D-Sec GmbH to perform premium
security services.
X41 has the following references that show their experience in the field:
The testers at X41 have extensive experience with penetration testing and red teaming exercises
in complex environments. This includes enterprise environments with thousands of users and
vendor infrastructures such as the Mozilla Firefox Updater (Balrog).
Fields of expertise in the area of application security encompass security-centered code reviews,
binary reverse-engineering and vulnerability-discovery. Custom research and IT security consult-
ing, as well as support services, are the core competencies of X41. The team has a strong techni-
cal background and performs security reviews of complex and high-profile applications such as
Google Chrome and Microsoft Edge web browsers.
1 https://blog.mozilla.org/security/2018/10/09/trusting-the-delivery-of-firefox-updates/
2 https://browser-security.x41-dsec.de/X41-Browser-Security-White-Paper.pdf
3 https://www.x41-dsec.de/reports/Kudelski-X41-Wire-Report-phase1-20170208.pdf
4 https://www.x41-dsec.de/lab/blog/fax/
5 https://2018.zeronights.ru/en/reports/zero-fax-given/
6 https://www.x41-dsec.de/lab/blog/smartcards/
Acronyms
A Fuzzing
This appendix describes the various fuzz tests performed by X41. These were not run in-depth
since the main focus of this assessment was a manual code audit. Fuzz testing can be further
improved by reducing the amounts of leaked memory in error handling and performing the testing
with various settings. Additionally, when commands change the state of the repository, it is hard
to reproduce errors, so one might want to disable the write-codepaths. When disabling the write-
codepaths these will not be covered by the fuzz testing.
Fuzzing credential_from_url_gently()
1 #include <stddef.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include "credential.h"
7
8 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
9
10 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
11 {
12 char *buf;
13 if (size < 2)
14 return 0;
15
16 buf = malloc(size);
17 if (!buf)
18 return 0;
19
20 memcpy(buf, data, size);
21 buf[size-1] = 0;
22
23
24 // start fuzzing
25 struct credential c;
26 int res;
27
28 credential_init(&c);
29
30 res = credential_from_url_gently(&c, buf, 1);
31
32 credential_clear(&c);
33
34 // cleanup
35 free(buf);
36
37 return 0;
38 }
Fuzzing url_decode_mem()
1 #include <stddef.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include "url.h"
7
8 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
9
10 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
11 {
12 char *buf;
13 if (size < 2)
14 return 0;
15
16 buf = malloc(size);
17 if (!buf)
18 return 0;
19
20 memcpy(buf, data, size);
21
22
23 // start fuzzing
24 char *r;
25 r = url_decode_mem(buf, size);
26 free(r);
27
28 buf[size-1] = 0;
29 r = url_decode(buf);
30 free(r);
31
32 r = url_percent_decode(buf);
33 free(r);
34
35 // cleanup
36 free(buf);
37
38 return 0;
39 }
Fuzzing parse_attr_line()
This testcase required the export of parse_attr_line() as well since its by default a static function.
1 #include <stddef.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <string.h>
5 #include "attr.h"
6
7
8 #ifndef READ_ATTR_NOFOLLOW
9 /* Flags usable in read_attr() and parse_attr_line() family of functions. */
10 #define READ_ATTR_MACRO_OK (1<<0)
11 #define READ_ATTR_NOFOLLOW (1<<1)
12 #endif
13
14 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
15
16 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
17 {
18 struct match_attr *res;
19 char *path = "/tmp/test/";
20 int lineno = 0;
21 unsigned flags = READ_ATTR_NOFOLLOW;
22 char *buf;
23 if (size < 2)
24 return 0;
25
26 buf = malloc(size);
27 if (!buf)
28 return 0;
29
30 memcpy(buf, data, size);
31
32 buf[size-1] = 0;
33
34 res = parse_attr_line(buf, path, lineno, flags);
35
36 if (res) {
37 int j;
38 for (j = 0; j < res->num_attr; j++) {
39 const char *setto = res->state[j].setto;
40 if (ATTR_TRUE(setto) ||
41 ATTR_FALSE(setto) ||
42 ATTR_UNSET(setto) ||
43 ATTR_UNKNOWN(setto))
44 ;
45 else
46 free((char *) setto);
47
48 }
49 free(res);
50 }
51 free(buf);
52
53 return 0;
54 }
Fuzzing apply_patch()
To fuzz test the parsing of patches more efficiently, a libfuzzer harness was created by patching
apply_patch() to receive a stringbuffer as additional parameter. When this parameter was not
NULL, the call to read_patch_file() was omitted and data used from the supplied stringbuffer. Due
to many memory leaks in the code in error handling routines, the fuzzers ran out of the supplied
2GB of memory every 700.000 iterations and needed to be restarted.
The commands git-apply, git-status and git-unpack-objects operate on files which are zlib
compressed by default. After replacing the zlib wrapper zlib.c with a dummy that only performs
memcpy() it was possible to use AFL++ on these files more efficiently, since the uncompressed
parts could be fuzzed and no additional checksum requirements were in place. Please be aware
that the patch does have issues with some of the commands, git-push fails for unknown reasons.
1 /*
2 * zlib wrappers to make sure we don't silently miss errors
3 * at init time.
4 */
5 #include "cache.h"
6
7 void git_inflate_init(git_zstream *strm)
8 {
9 return;
10 }
11
12 void git_inflate_init_gzip_only(git_zstream *strm)
13 {
14 return;
15 }
16
17 void git_inflate_end(git_zstream *strm)
18 {
19 return;
20 }
21
22 int git_inflate(git_zstream *strm, int flush)
23 {
24 size_t len = strm->avail_out >= strm->avail_in? strm->avail_in: strm->avail_out;
25 int status = Z_OK;
26
27 if (strm->next_out == Z_NULL || strm->next_in == Z_NULL) {
28 status = Z_STREAM_ERROR;
29 goto out;
30 }
31 if (flush == Z_FINISH && strm->avail_in == 0) {
32 status = Z_STREAM_END;
33 goto out;
34 }
35
36 if (strm->avail_in == 0) {
37 status = Z_BUF_ERROR;
38 goto out;
39 }
40
41 if (strm->avail_out == 0) {
42 status = Z_MEM_ERROR;
43 goto out;
44 }
45
46 memcpy(strm->next_out, strm->next_in, len);
47
48 strm->next_out += len;
49 strm->next_in += len;
50 strm->avail_out -= len;
51 strm->avail_in -= len;
52 strm->total_out += len;
53 strm->total_in += len;
54
55 if (flush == Z_FINISH && strm->avail_in == 0)
56 status = Z_STREAM_END;
57
58 out:
59 return status;
60 }
61
62 #define deflateBound(s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
63 unsigned long git_deflate_bound(git_zstream *strm, unsigned long size)
64 {
65 return deflateBound(size);
66 }
67
68 void git_deflate_init(git_zstream *strm, int level)
69 {
70 memset(strm, 0, sizeof(*strm));
71 return;
72 }
73
74
75 void git_deflate_init_gzip(git_zstream *strm, int level)
76 {
77 memset(strm, 0, sizeof(*strm));
78 return;
79 }
80
81 void git_deflate_init_raw(git_zstream *strm, int level)
82 {
83 memset(strm, 0, sizeof(*strm));
84 return;
85 }
86
87 int git_deflate_abort(git_zstream *strm)
88 {
89 return Z_OK;
90 }
91
92 void git_deflate_end(git_zstream *strm)
93 {
94 return;
95 }
96
97 int git_deflate_end_gently(git_zstream *strm)
98 {
99 return Z_OK;
100 }
101
102 int git_deflate(git_zstream *strm, int flush)
103 {
104 size_t len;
105 int status = Z_OK;
106
107 len = strm->avail_out >= strm->avail_in? strm->avail_in: strm->avail_out;
108
109
110 if (strm->next_out == Z_NULL || strm->next_in == Z_NULL) {
111 status = Z_STREAM_ERROR;
112 goto out;
113 }
114
115 if (strm->avail_in == 0) {
116 status = Z_BUF_ERROR;
117 goto out;
118 }
119
120 if (strm->avail_out == 0) {
121 status = Z_BUF_ERROR;
122 goto out;
123 }
124
125 memcpy(strm->next_out, strm->next_in, len);
126
127 strm->next_out += len;
128 strm->next_in += len;
129 strm->avail_out -= len;
130 strm->avail_in -= len;
131 strm->total_out += len;
132 strm->total_in += len;
133
134 if (flush == Z_FINISH && strm->avail_in == 0)
135 status = Z_STREAM_END;
136
137 out:
138 return status;
139 }
Since several issues were found in the handling of format string specifiers for pretty_print_commit()
this function was fuzz tested as well. Since the function requires a commit as argument the setup
for libfuzzer seemed too complex so it was decided to use AFL++ instead. revision.c was mod-
ified to read the format specifier from stdin instead of the commandline. This allowed to fuzz
test git-log format="xx" HEAD on an existing repository.
Since some of the format strings require huge amounts of memory, some fuzz cases can only be
found without the address sanitizer. Additionally, it is advised to increase the timeout value since
some testcases require some processing time before they finally crash.