Repository navigation
Conversation
…es, stream text (VC-801) read opened every file whole in Electron main before its 2,000-line / 50 KB cut. It now opens a FileSystem.openFileReader (non-blocking open judged by fstat, so a FIFO or device behind a symlink is refused at once), sniffs the first 8 KiB for an image signature (unchanged image path) or a NUL byte (binary notice), and otherwise streams the file in 256 KiB reads into one reused buffer, decoding only the prefix the cut can show.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…0 MB test (VC-801 review) - openFileReader stats the path (following links) and refuses a directory, FIFO, device or socket before open; opens O_NONBLOCK | O_NOCTTY and keeps the fstat on the descriptor as the swap guard. - read answers a NUL-bearing head that opens with a UTF-16 BOM with an iconv hint instead of the binary notice; countLf replaces the duplicated scan. - The large-file tests drop on-disk fixtures and RSS sampling for a virtual 500 MB FileByteReader: no read exceeds the chunk, one buffer is reused, and truncateHead only ever sees the bounded kept prefix.
…y (VC-801) A stat before open drew CodeQL js/file-system-race (high), and docs/development/codeql-triage.md prescribes the descriptor check. open stays O_NONBLOCK | O_NOCTTY with fstat as the one check; a socket, which cannot be opened and fails with a platform-specific errno, is named from a stat taken after the failure, to choose the words only.
…e (VC-801) The middle-offset check moves to its own 16 MB virtual file; the two 500 MB scans get a 120 s budget, since coverage instrumentation slows the 26 M-line LF scan past the 5 s default.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem (VC-801)
readcalledenv.readBinaryFile(readFile, no stat, no cap) and decoded, split and re-joined the whole file before its 2,000-line / 50 KB cut. All of that runs in Electron main./dev/zeroor to a FIFO:readFilereads to an EOF that never comes, or blocks waiting for a writer. Verified on main: the symlinked-FIFO test hangs. Named directly, both were already refused, becauseresolveReadToolPath'sexists→lstatrejects their file type. The ticket's direct-path sequences 2 and 3 are only reachable through a link.Fix
FileSystem.openFileReader(new; vendoredtypes.ts,env/nodejs.ts) opens withO_RDONLY | O_NONBLOCK | O_NOCTTYand judges the file withfstaton the open descriptor:is_directory. FIFOs and devices →invalid("Not a regular file …").stated afterwards, only to choose the same "Not a regular file" words.read(position, length, context, into?)and never throws.ScopedExecutionEnvguards and delegates it likereadBinaryFile.refusingCredentialReadsadds it to the refused reads.readopens the reader and sniffs the first 8 KiB:[Binary file (N), not shown as text. Use bash: file <path>, or xxd <path> | head -c 51200]. If the file also starts with a UTF-16 BOM (FF FE/FE FF), it answers[UTF-16 text (N), not shown as text. Use bash: iconv -f UTF-16 -t UTF-8 <path> | head -c 51200]instead.truncateHead, with only its totals replaced.markingSavedOutputare untouched.Decisions
openTextLineReader. That reader buffers a whole line, so one 500 MB minified line would still be held in full, andScopedExecutionEnvrefuses it. A positional byte reader bounds both cases. It also lets the type check happen on the open descriptor, which avoids a TOCTOU race and a blocking FIFO open.statbeforeopen. Review asked for astatbeforeopen, keepingfstatas the swap guard. I pushed that as 5a7d5a9. CodeQL then raised a new highjs/file-system-racealert on it ("The file may have changed since it was checked").docs/development/codeql-triage.mdprescribes exactly the descriptor-only design: "Checking and consuming the same descriptor closes pathname check/use races … reject FIFO/directory targets through nonblocking open andfstat." So the descriptor check stays the only check.O_NOCTTY(from the review) covers the tty side effect of opening a device, and the socket refusal is kept by the post-failurestat. The guarantee is about the file type only.ScopedExecutionEnv's workspacelstatwalk followed by theopenis still racy, as it was before this change.details.truncation.totalBytesand an oversized first line's size are counted in the file's own bytes, less a leading BOM. For valid UTF-8 that is the same number. Decoding everything counted each invalid byte as a 3-byte U+FFFD; matching that would mean decoding the whole file, which is the allocation churn this removes. No caller readstotalBytesforread.limitselects no lines. It used to count from the end, an accident ofArray.prototype.slice. The surrounding hint numbers are computed exactly as before.pi-vendor-tools-coverage.test.ts"propagates read failures" now injects its failure onopenFileReaderinstead ofreadBinaryFile, becausereadno longer callsreadBinaryFile. Its assertions are unchanged. The other read tests (truncation messages, offset/limit, images, saved-output marking) pass without edits.vendor/pi-harness/README.md, with a note inUPSTREAM.md.Tests
New
src/pi/read-bounded.test.ts(37 tests):FileByteReaderserves a repeating pattern, so nothing that size touches disk or memory. The test checks:[Showing lines 1-2000 of 26214401. Use offset=2001 to continue.];truncateHead(wrapped throughvi.mock) is called once, on at most 51,201 + 262,144 characters;[Line 1 is 500.0MB …]message and totals, under the same bounds./dev/zeroand FIFOs, directly and through a symlink, plus a socket behind a symlink, directories and a symlink to a regular file.FF/FE.openFileReaderitself: positional and short reads, close, cancellation at each stage, injected stat/read/close failures, the descriptor check, the open flags (O_NOCTTY), opens that fail for other reasons, scoped guard and credential refusal.Fails on main (one run of the file,
-t-filtered): the symlinked-FIFO test times out "still waiting", the binary test receives decodedAAAA…, and the directory test gets Node's EISDIR message. The/dev/zero-symlink test was deliberately not run against main, because the old code would allocate up to 2 GB.Run locally, one file at a time with
--maxWorkers=1, all green:read-boundedpi-vendor-tools-coveragevendor-tools-edgetools-vendortool-pathcredential-envscoped-execution-envpi-vendor-env-coveragetoolsAlso
vp checkon the touched files. Typecheck and coverage are left to CI.Overlap:
git merge-treeis clean against origin/main and against #909, #919, #920, #921 and #922. #651, an older open PR touchingscoped-execution-env.ts, conflicts with this branch on exactly the 56 files it already conflicts on with main, so nothing new. Targets main.Follow-ups (not in this PR)
size > 0, because procfs-style files report 0.offsetshows up as-is in the hint numbers. Clamp it in the schema later.