Skip to content

fix(kubectl-plugin/log): validate header.Mode and release fds per iteration#4728

Open
SAY-5 wants to merge 2 commits intoray-project:masterfrom
SAY-5:fix-g115-fdleak-log-4695
Open

fix(kubectl-plugin/log): validate header.Mode and release fds per iteration#4728
SAY-5 wants to merge 2 commits intoray-project:masterfrom
SAY-5:fix-g115-fdleak-log-4695

Conversation

@SAY-5
Copy link
Copy Markdown

@SAY-5 SAY-5 commented Apr 17, 2026

Fixes #4695.

What

ClusterLogOptions.downloadRayLogFiles in kubectl-plugin/pkg/cmd/log/log.go had three issues called out in the bug:

  1. gosec G115 (integer overflow). tar.Header.Mode is int64, os.FileMode is uint32. The existing overflow check printed a warning but then fell through to os.FileMode(header.Mode) anyway, so a crafted tar header could still apply unexpected mode bits. Now we continue when the mode is out of range and cast explicitly through uint32 so the G115 diagnostic is honoured:

    if header.Mode < 0 || header.Mode > math.MaxUint32 {
        fmt.Fprintf(..., "file mode %d outside of acceptable range, skipping file %s\n", header.Mode, header.Name)
        continue
    }
    outFile, err := os.OpenFile(localFilePath, os.O_CREATE|os.O_RDWR, os.FileMode(uint32(header.Mode)))
  2. File-descriptor leak. defer outFile.Close() sat inside the extraction loop, so every opened file stayed open until downloadRayLogFiles returned. Extracting a large log tar can hit the too many open files ulimit. The copy is now wrapped in an IIFE so the defer fires at the end of each iteration:

    copyErr := func() error {
        defer outFile.Close()
        for {
            ...
        }
    }()
  3. Error message quality. out sideoutside, accceptableacceptable, include the offending filename, add a newline.

Why

(1) is a security improvement (gosec was right), (2) is a reliability improvement for large log tars, and (3) is housekeeping that falls out of touching the surrounding block anyway.

Signed off per DCO.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 145d519. Configure here.

Comment thread kubectl-plugin/pkg/cmd/log/log.go Outdated
…ration

downloadRayLogFiles had three related problems flagged in ray-project#4695:

1. gosec G115: tar.Header.Mode is int64 but os.FileMode is uint32. The
   existing overflow check was non-fatal: it printed a warning and
   then proceeded to cast the overflowing value anyway. A malformed
   or hostile tar header could therefore ask for unexpected mode
   bits. Reject the file early (using 'break', not 'continue', so the
   for-loop below still calls tarReader.Next and does not spin on the
   bad entry) and cast explicitly through uint32 so gosec is happy.

2. File-descriptor leak: outFile.Close was registered with 'defer'
   inside the extraction loop, so every opened file stayed open
   until downloadRayLogFiles returned. Extracting a large log tar
   could hit the 'too many open files' ulimit. Wrap the per-file
   copy in a closure so the deferred Close runs at end of iteration.

3. Minor: fix the user-visible message ('out side' -> 'outside',
   'accceptable' -> 'acceptable', include the file name, add a
   newline).

Fixes ray-project#4695

Signed-off-by: Sai Asish Y <say.apm35@gmail.com>
@SAY-5
Copy link
Copy Markdown
Author

SAY-5 commented Apr 17, 2026

Thanks @cursor for catching this — that was a real bug. continue inside the switch continues the outer for but skips tarReader.Next() below, so a bad header.Mode would spin forever.

Fixed by using break to exit just the switch, which lets execution fall through to the tarReader.Next() that advances to the next entry. Also left an inline comment explaining the subtlety so future readers don't regress it. Force-pushed.

@SAY-5 SAY-5 force-pushed the fix-g115-fdleak-log-4695 branch from c11b8c9 to 53dc837 Compare April 17, 2026 07:48
@hango880623
Copy link
Copy Markdown
Contributor

Since this changes the behavior in log.go, it’d be great to add a couple of tests in log_test.go to cover the new cases:

  • skipping files with invalid Mode values (e.g. > math.MaxUint32)

  • making sure extraction keeps going after hitting one of those

  • maybe updating the test helper to make it easier to create tar files with different modes / lots of entries

…ject#4728 review

@hango880623 review asked for:
* a test that exercises the invalid-Mode skip path;
* one that proves extraction continues past the bad entry;
* a more flexible tar test helper.

Refactor the existing createFakeTarFile() onto a new
buildFakeTarFile([]fakeTarEntry) helper so tests can drop
arbitrary entries (modes, ordering, etc.) without redefining
the boilerplate. The original helper is preserved as a thin
wrapper so the existing TestDownloadRayLogFiles test does not
move.

Add TestDownloadRayLogFiles_SkipsInvalidMode: builds a tar with a
valid entry (good_before.txt), an out-of-range Mode entry
(math.MaxUint32+1, the smallest int64 that fails the bounds
check), and a third valid entry (good_after.txt). Asserts:

* downloadRayLogFiles returns no error;
* good_before.txt and good_after.txt land on disk;
* bad_mode.txt is skipped (not written).

This pins the regression behind the G115 fix in this PR — both
the bounds rejection and the 'fall through to tarReader.Next()
instead of spinning' fix from the earlier review round.

Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com>
@SAY-5
Copy link
Copy Markdown
Author

SAY-5 commented May 1, 2026

Hi @hango880623, thanks for the review.

Pushed both pieces in 6a86fc4d:

  1. More flexible test helper — extracted buildFakeTarFile([]fakeTarEntry) so tests can drop arbitrary entries (modes, ordering, etc.) directly. The existing createFakeTarFile() is preserved as a thin wrapper so the original TestDownloadRayLogFiles keeps running unchanged.

  2. Regression testTestDownloadRayLogFiles_SkipsInvalidMode builds a tar with three entries: a valid one (good_before.txt), an out-of-range mode entry (math.MaxUint32 + 1 — the smallest int64 that fails the bounds check), and a third valid one (good_after.txt). It asserts:

    • downloadRayLogFiles returns no error;
    • good_before.txt and good_after.txt land on disk;
    • bad_mode.txt is skipped (not written).

    That pins both halves of the fix — the bounds rejection and the break-instead-of-continue change so a bad entry doesn't loop forever.

go test -run TestDownloadRayLogFiles -v ./kubectl-plugin/pkg/cmd/log/ → 2/2 passing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Fix G115 integer overflow and file descriptor leak in log extraction

2 participants