Introduction
A small branch pushed to a Git forge could be enough to make the server write gigabytes of attacker-controlled data past the end of a heap buffer, simply because the forge checked whether a merge request was mergeable. The trigger is a single committed .gitattributes line that inflates conflict markers until Git's 32-bit merge output counter wraps around and the resulting allocation is far too small.
The issue is tracked as CVE-2026-105437, classified as CWE 122 (Heap-based Buffer Overflow), and rated CVSS 3.1 7.5 (High) by GitLab Inc., which acted as CNA. The affected product is upstream Git, not a GitLab-specific component. The documented minimum impact is a crash. Because the overflow is large and its contents are largely attacker-controlled, the advisory notes that worse heap corruption is possible. Reliable code execution has not been demonstrated in the public material.
Technical information
Root cause: two passes that must agree on one 32-bit total
Git's textual merge output is produced by xdl_fill_merge_buffer() in xdiff/xmerge.c, and that function runs twice:
- The first call passes
dest == NULLand only adds up how many bytes the merge result needs. - The caller then calls
malloc()with that number. - The second call walks the same hunks again and writes each piece at
dest + size.
This design is only safe if both passes agree on the total. Before the fix, that total was an int, which is 32 bits even on 64-bit platforms.
The amplifier is the conflict-marker-size attribute. It can be set from a committed .gitattributes file, it accepts any positive integer, and the marker length is added several times for every conflict (the opening, base, separator and closing markers). A small file with a huge marker size and many separate conflicts can therefore push the real output size past 4 GiB. In the words of the fix commit, the sum "wraps back to a small positive value", malloc() succeeds with a buffer that is too small, and the write pass runs past the end of the buffer and later lands before its start.
For defenders, the important property is that input size and output size diverge. The data an attacker pushes is small, and the oversized output only materializes during the merge.
Concrete trigger shape
The upstream regression tests commit a .gitattributes with conflict-marker-size=3495254 and create two branches, each changing every fifth line of a 2,100-line file. That yields 420 separate conflicts, and the merged file comes out just over 4 GiB, enough to exceed a 32-bit counter.
Attack flow
Based on the advisory, exploitation follows this sequence:
- Control a repository. The attacker commits a
.gitattributesentry setting a very largeconflict-marker-sizefor a target file. - Prepare conflicting branches. Two branches modify many separate regions of the same small file so a merge produces many independent conflicts.
- Cause a textual three-way merge. Any command that does this can trigger the bug, including
git merge,git rebase,git cherry-pickandgit merge-tree. - Sizing pass wraps. The 32-bit total overflows to a small value and
malloc()returns an undersized buffer. - Write pass overflows. Attacker-controlled merge output, mostly marker bytes and conflicting content, is written outside the buffer.
The advisory singles out Git forges as the most likely target. Forges may run git merge-tree on the server to check mergeability. When the server reads attributes from trees in the repository (for example through attr.tree or --attr-source), any user who can push branches and request that they be merged, for example by opening a pull or merge request, can trigger the overflow without anyone else doing anything.
Reading the CVSS vector
The published vector is:
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
The vector includes UI:R, while the advisory text separately describes a server workflow that needs no action from another person. We read these as two different interaction contexts: client-side merges of an attacker repository, and unattended forge mergeability checks. Exposure should be assessed by whether your deployment performs textual merges on untrusted content and sources attributes from untrusted trees, not simply by whether Git is installed.
Exploitation status
The CVE record credits taise for reporting the issue through HackerOne report 4044249, tagged technical-description, exploit and permissions-required. The report body is not publicly accessible. The reviewed sources identify no threat actor, campaign or observed intrusion, and as of November 10, 2026 the CVE is not in CISA's Known Exploited Vulnerabilities catalog. A public, validated proof of concept is not confirmed.
Patch information
Upstream Git fixed this in a three-commit series by Kaartic Sivaraam, based on a patch by Jeff King and committed by Johannes Schindelin. It shipped in Git 2.56.1, with backports to 2.55.1, 2.54.1, 2.53.1, 2.52.1, 2.51.3 and 2.50.2. The 2.56.1 release notes list CVE-2026-105437 among the six CVEs fixed in this round. GitHub's advisory summarizes the fix as moving to appropriately sized types with checked arithmetic, so that merges whose output size cannot be represented safely are rejected.
Commit 1: 0dc3b5c, a five-operand overflow helper
Git already has st_add(), st_add3() and st_add4() in git-compat-util.h. These perform size_t addition and stop Git with an error instead of wrapping. One line in fill_conflict_hunk() adds five terms together, so the patch adds a five-operand version:
#define st_add3(a,b,c) st_add(st_add((a),(b)),(c)) #define st_add4(a,b,c,d) st_add(st_add3((a),(b),(c)),(d)) +#define st_add5(a,b,c,d,e) st_add(st_add4((a),(b),(c),(d)),(e))
Commit 2: 288266c, "xdiff: use size_t for merge buffer size computations"
This commit changes the size-tracking variables and return types from int to size_t across xdl_recs_copy_0(), xdl_recs_copy(), xdl_orig_copy(), fill_conflict_hunk() (both its size parameter and its return value) and xdl_fill_merge_buffer():
-static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1, ... +static size_t xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1, ... - int size, i; + size_t size; + int i;
Because size_t is unsigned, the running total can no longer go negative, and on 64-bit systems it cannot realistically wrap. One more problem remained. The result goes into mmbuffer_t, whose size field is a long, and a long is only 32 bits on Windows. Rather than truncate silently, xdl_do_merge() now gives up when the size will not fit:
- int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2, + size_t size = xdl_fill_merge_buffer(xe1, name1, xe2, name2, ancestor_name, favor, changes, NULL, style, marker_size); + if (size > LONG_MAX) { + xdl_cleanup_merge(changes); + return -1; + } result->ptr = xdl_malloc(size);
The merge then fails with an error that callers already handle. The commit message notes that a full move of mmbuffer_t to size_t is planned as separate work.
Commit 3: b131387, "xdiff: check for overflow when computing merge buffer size"
Commit 2 left two gaps:
- Marker terms such as
marker_size + 1 + needs_cr + marker1_sizewere still summed asintbefore being added tosize. Withconflict-marker-sizeclose toINT_MAX, that inner sum goes negative. Converted tosize_tand added, it makessizesmaller, even on 64-bit systems. - On 32-bit platforms,
size_tis 32 bits, so the total can still wrap.
The fix routes every addition in the sizing pass through the checked helpers and adds each marker term separately, so the inner int sum never happens:
if (!dest) { - size += marker_size + 1 + needs_cr + marker1_size; + size = st_add5(size, marker_size, 1, needs_cr, marker1_size);
- for (i = 0; i < count; size += recs[i++]->size) + for (i = 0; i < count; size = st_add(size, recs[i++]->size))
The same change applies to the ||||||| and ======= markers (st_add5 and st_add4), the >>>>>>> marker, and every xdl_recs_copy() / xdl_orig_copy() call in fill_conflict_hunk() and xdl_fill_merge_buffer().
The memset(dest + size, ...) lines in the write pass deliberately keep plain +=. Once the sizing pass finishes without error, the write pass repeats exactly the same additions into a buffer of exactly that size, so it cannot overflow. As the commit puts it: "We need only perform the overflow checks on the first round."
How the fix is tested
Both commits add tests to t/t4300-merge-tree.sh, marked EXPENSIVE, using the 420-conflict setup described above, whose merged output is just over 4 GiB. Expected results by platform:
| Platform | Expected result |
|---|---|
64-bit size_t and long | git merge-tree --write-tree finishes cleanly, reports a conflict (exit code 1) and produces a valid tree |
64-bit size_t, 32-bit long (Windows) | Merge fails with failure to merge |
32-bit size_t | Merge fails with size_t overflow |
As a result, merge, rebase, cherry-pick and server-side merge-tree now stop with an error instead of writing past the end of the heap buffer.
Interim workarounds for servers
Servers that cannot upgrade right away have two options from the advisory. The first is to add this line to each repository's info/attributes:
* conflict-marker-size=7
Git's attribute documentation confirms that $GIT_DIR/info/attributes takes precedence over committed .gitattributes, and that the usual conflict marker length is 7 characters, so this neutralizes the malicious attribute at a higher-precedence layer. The second is to turn off server-side mergeability checks for untrusted repositories. Both constrain the trigger without fixing the arithmetic, so upgrading remains the actual correction.
Affected systems and versions
The CVE record lists the following affected ranges and fixed versions. Release-candidate starting points appear exactly as published:
| Affected range | Fixed version |
|---|---|
| Versions before 2.49.1 | 2.49.1 |
From 2.50.0-rc0, before 2.50.2 | 2.50.2 |
From 2.51.0-rc0, before 2.51.3 | 2.51.3 |
From 2.52.0-rc0, before 2.52.1 | 2.52.1 |
From 2.53.0-rc0, before 2.53.1 | 2.53.1 |
From 2.54.0-rc0, before 2.54.1 | 2.54.1 |
From 2.55.0-rc0, before 2.55.1 | 2.55.1 |
From 2.56.0-rc0, before 2.56.1 | 2.56.1 |
These are separate boundaries per release line, so "anything before 2.56.1" is not the correct test. The upstream patch announcement lists backports to 2.50.2 through 2.55.1 alongside 2.56.1, while the CVE record additionally lists 2.49.1. Confirm the exact build you run against your distribution or vendor packaging.
Vulnerable configurations:
- Any Git client performing a textual three-way merge of attacker-influenced content via
git merge,git rebase,git cherry-pickorgit merge-tree. - Git forges and hosting servers that run
git merge-treefor mergeability checks and read attributes from repository trees (for example viaattr.treeor--attr-source). This is the configuration where a user who can push and open a merge request can trigger the overflow unattended.
Git's security policy also states that older maintenance tracks have no formal guarantee of security updates, so lines not listed above should be treated as needing an upgrade to a fixed track.
Vendor security history
Git has had prior integer-related memory-safety bugs. GitLab describes an audit by X41 on behalf of OSTIF that identified CVE-2022-41903 in Git's formatting code (pretty.c), where a reproducer exceeded what an int could hold and triggered heap corruption. That finding went through the private Git security list, and GitLab Gitaly engineer Patrick Steinhardt developed fixes, found additional issues and extended fuzzing coverage. Separately, Git's July 2025 security announcement shipped fixes across multiple maintenance releases for vulnerabilities including CVE-2025-48384.
The pattern is a familiar one. int-sized arithmetic on attacker-influenced lengths gets handled through a coordinated private disclosure process, with fixes across several maintenance lines. The CVE-2026-105437 series follows the same approach and reuses the existing st_add family rather than inventing new checks.
References
- NVD entry for CVE-2026-105437
- CVE Record for CVE-2026-105437
- CVE Services API record
- GitHub Blog: Git security vulnerabilities announced
- Git 2.56.1 release notes
- Commit 0dc3b5c: st_add5 helper
- Commit 288266c: xdiff: use size_t for merge buffer size computations
- Commit b131387: xdiff: check for overflow when computing merge buffer size
- Git mailing list: xdiff: use size_t for buffer sizes (patch v2 3/7)
- GitLab Git work item 814
- GitLab work item 632121
- HackerOne report 4044249
- Git gitattributes documentation
- About Git
- Git security advisories on GitHub
- Git security policy
- GitLab: Git security audit, inside the hunt for CVEs
- oss security: Multiple vulnerabilities fixed in Git (July 2025)
- cve.now entry for CVE-2026-105437
- VulDB entry for CVE-2026-105437
- CISA Known Exploited Vulnerabilities Catalog



