Summary
GitLsFileOutputReader searches for the NUL delimiter across the entire 4096-character buffer rather than only the characters returned by the current read.
Impact
git standard output is pipe-backed and TextReader.Read may return fewer characters than requested before EOF. The unused or stale buffer tail can therefore be mistaken for a real NUL delimiter, splitting a single git ls-files -z record across multiple records. This can produce malformed paths or hashes, fail cache initialization, or leave files out of source-control-backed fingerprinting.
Evidence
In src/Common/SourceControl/GitFileHashProvider.cs, GitLsFileOutputReader.PopulateAsync uses:
Array.IndexOf(buffer, '\0', startIdx)
The search is not bounded by readCnt. Existing tests use StringReader, which does not exercise realistic partial pipe reads.
Suggested fix
Bound delimiter searches to the valid range, for example with the Array.IndexOf overload that accepts a count. Add a test TextReader that deliberately returns small chunks and verify records spanning reads are reconstructed exactly.
Acceptance criteria
- NUL-delimited records are parsed correctly regardless of read chunk boundaries.
- A test covers multiple partial reads, including a split within both staging metadata and a path.
- Existing source-control hashing tests continue to pass.
Summary
GitLsFileOutputReadersearches for the NUL delimiter across the entire 4096-character buffer rather than only the characters returned by the current read.Impact
gitstandard output is pipe-backed andTextReader.Readmay return fewer characters than requested before EOF. The unused or stale buffer tail can therefore be mistaken for a real NUL delimiter, splitting a singlegit ls-files -zrecord across multiple records. This can produce malformed paths or hashes, fail cache initialization, or leave files out of source-control-backed fingerprinting.Evidence
In
src/Common/SourceControl/GitFileHashProvider.cs,GitLsFileOutputReader.PopulateAsyncuses:The search is not bounded by
readCnt. Existing tests useStringReader, which does not exercise realistic partial pipe reads.Suggested fix
Bound delimiter searches to the valid range, for example with the
Array.IndexOfoverload that accepts a count. Add a testTextReaderthat deliberately returns small chunks and verify records spanning reads are reconstructed exactly.Acceptance criteria