Skip to content

Commit

Permalink
pkg/cdi: add test case for EMFILE crash (avoidance).
Browse files Browse the repository at this point in the history
Signed-off-by: Krisztian Litkey <krisztian.litkey@intel.com>
  • Loading branch information
klihub committed Feb 22, 2025
1 parent 039c460 commit 3a3ea2e
Showing 1 changed file with 92 additions and 0 deletions.
92 changes: 92 additions & 0 deletions pkg/cdi/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"syscall"
"testing"
"time"

Expand Down Expand Up @@ -812,6 +814,96 @@ devices:
}
}

func TestTooManyOpenFiles(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("Test not applicable on Windows")
}

em, err := triggerEmfile()
require.NoError(t, err)
require.NotNil(t, em)
_, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
require.Equal(t, syscall.EMFILE, err)

cache := newCache(
WithAutoRefresh(true),
)
require.NotNil(t, cache)

// try to trigger original crash with a nil fsnotify.Watcher
_, _ = cache.InjectDevices(&oci.Spec{}, "vendor1.com/device=dev1")

require.NoError(t, em.undo())
}

type emfile struct {
limit syscall.Rlimit
fds []int
}

func getProcStatusFdSize() (uint64, error) {
status, err := os.ReadFile("/proc/self/status")
if err != nil {
return 0, err
}

const fdSizeTag = "FDSize:"

for _, line := range strings.Split(string(status), "\n") {
if strings.HasPrefix(line, fdSizeTag) {
value := strings.TrimSpace(strings.TrimPrefix(line, fdSizeTag))
size, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return 0, err
}
return size, nil
}
}

return 0, fmt.Errorf("tag %s not found in /proc/self/status", fdSizeTag)
}

func triggerEmfile() (*emfile, error) {
fdsize, err := getProcStatusFdSize()
if err != nil {
return nil, err
}

em := &emfile{}

if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &em.limit); err != nil {
return nil, err
}

limit := em.limit
limit.Cur = fdsize

if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
return nil, err
}

for {
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
if err != nil {
break
}
em.fds = append(em.fds, fd)
}

return em, nil
}

func (em *emfile) undo() error {
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &em.limit); err != nil {
return err
}
for _, fd := range em.fds {
syscall.Close(fd)
}

return nil
}

func TestInjectDevice(t *testing.T) {
type specDirs struct {
etc map[string]string
Expand Down

0 comments on commit 3a3ea2e

Please sign in to comment.