Description
findLineInFile() in tests/e2e/utils.go opens a file via os.Open() but never closes it. The opened file handle is leaked on every invocation, regardless of whether the search pattern is found or not.
This function is called by multiple e2e test functions — userGroupTest() calls it 3 times (for Uid, Gid, Groups), and seccompTest() calls it once (for Seccomp). Across a full e2e suite run with 40+ test cases, this silently leaks dozens of file descriptors against /proc//status files.While Go's garbage collector will eventually finalize the *os.File object, this is non-deterministic and not guaranteed to happen before the process exhausts its file descriptor limit — especially on CI runners with conservative ulimit -n settings.
Root Cause
In tests/e2e/utils.go, the findLineInFile function:
func findLineInFile(filePath string, pattern string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("Failed to open %s: %v", filePath, err)
}
// missing: defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, pattern) {
return line, nil // file never closed on success path
}
}
return "", fmt.Errorf("Pattern %s was not found in any line of %s", pattern, filePath)
// file never closed on failure path either
}
Both the early-return (pattern found) and the fall-through (pattern not found) paths exit without closing the file.
Proposed Fix
Add defer file.Close() immediately after the error check:
func findLineInFile(filePath string, pattern string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("Failed to open %s: %v", filePath, err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
// ... rest unchanged
}
This ensures the file handle is released on all code paths — success, failure, and panic.
System info
- Repository: urunc_test
- File: tests/e2e/utils.go, line 243-259
- Arch: Any (bug is in test utility code, not architecture-specific)
- VMM: Any (bug is in e2e test framework, not VMM-specific)
- Unikernel: Any (bug is in e2e test framework, not unikernel-specific)
Steps to reproduce
- Run the full e2e test suite:
- During test execution, inspect the test process's open file descriptors:
TEST_PID=$(pgrep -f "go test.*e2e")
ls /proc/$TEST_PID/fd | wc -l
-
Observe that the FD count increases by 1-4 per test case (depending on how many times findLineInFile is called) and never decreases during the run.
-
Expected behavior: File descriptor count remains stable as files are opened and promptly closed after each search.
-
Actual behavior: File descriptor count monotonically increases throughout the suite, leaking one FD per findLineInFile call.
Description
findLineInFile() in tests/e2e/utils.go opens a file via os.Open() but never closes it. The opened file handle is leaked on every invocation, regardless of whether the search pattern is found or not.
This function is called by multiple e2e test functions — userGroupTest() calls it 3 times (for Uid, Gid, Groups), and seccompTest() calls it once (for Seccomp). Across a full e2e suite run with 40+ test cases, this silently leaks dozens of file descriptors against /proc//status files.While Go's garbage collector will eventually finalize the *os.File object, this is non-deterministic and not guaranteed to happen before the process exhausts its file descriptor limit — especially on CI runners with conservative ulimit -n settings.
Root Cause
In tests/e2e/utils.go, the findLineInFile function:
Both the early-return (pattern found) and the fall-through (pattern not found) paths exit without closing the file.
Proposed Fix
Add defer file.Close() immediately after the error check:
This ensures the file handle is released on all code paths — success, failure, and panic.
System info
Steps to reproduce
Observe that the FD count increases by 1-4 per test case (depending on how many times findLineInFile is called) and never decreases during the run.
Expected behavior: File descriptor count remains stable as files are opened and promptly closed after each search.
Actual behavior: File descriptor count monotonically increases throughout the suite, leaking one FD per findLineInFile call.