Skip to content

Commit c0e6a8a

Browse files
committed
pkg/cdi: add test case for EMFILE crash (avoidance).
Signed-off-by: Krisztian Litkey <[email protected]>
1 parent 039c460 commit c0e6a8a

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed

pkg/cdi/cache_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import (
2121
"fmt"
2222
"os"
2323
"path/filepath"
24+
"runtime"
2425
"strconv"
2526
"strings"
2627
"sync"
28+
"syscall"
2729
"testing"
2830
"time"
2931

@@ -812,6 +814,102 @@ devices:
812814
}
813815
}
814816

817+
func TestTooManyOpenFiles(t *testing.T) {
818+
if runtime.GOOS != "linux" {
819+
t.Skip("Test not applicable on Windows")
820+
}
821+
822+
em, err := triggerEmfile()
823+
require.NoError(t, err)
824+
require.NotNil(t, em)
825+
defer func() {
826+
require.NoError(t, em.undo())
827+
}()
828+
829+
_, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
830+
require.Equal(t, syscall.EMFILE, err)
831+
832+
cache := newCache(
833+
WithAutoRefresh(true),
834+
)
835+
require.NotNil(t, cache)
836+
837+
// try to trigger original crash with a nil fsnotify.Watcher
838+
_, _ = cache.InjectDevices(&oci.Spec{}, "vendor1.com/device=dev1")
839+
}
840+
841+
type emfile struct {
842+
limit syscall.Rlimit
843+
fds []int
844+
}
845+
846+
func getProcStatusFdSize() (uint64, error) {
847+
status, err := os.ReadFile("/proc/self/status")
848+
if err != nil {
849+
return 0, err
850+
}
851+
852+
const fdSizeTag = "FDSize:"
853+
854+
for _, line := range strings.Split(string(status), "\n") {
855+
if strings.HasPrefix(line, fdSizeTag) {
856+
value := strings.TrimSpace(strings.TrimPrefix(line, fdSizeTag))
857+
size, err := strconv.ParseUint(value, 10, 64)
858+
if err != nil {
859+
return 0, err
860+
}
861+
return size, nil
862+
}
863+
}
864+
865+
return 0, fmt.Errorf("tag %s not found in /proc/self/status", fdSizeTag)
866+
}
867+
868+
func triggerEmfile() (*emfile, error) {
869+
fdsize, err := getProcStatusFdSize()
870+
if err != nil {
871+
return nil, err
872+
}
873+
874+
em := &emfile{}
875+
876+
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &em.limit); err != nil {
877+
return nil, err
878+
}
879+
880+
limit := em.limit
881+
limit.Cur = fdsize
882+
883+
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
884+
return nil, err
885+
}
886+
887+
for i := uint64(0); i < fdsize; i++ {
888+
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)
889+
if err != nil {
890+
return em, nil
891+
}
892+
em.fds = append(em.fds, fd)
893+
}
894+
895+
return nil, fmt.Errorf("failed to trigger EMFILE")
896+
}
897+
898+
func (em *emfile) undo() error {
899+
if em == nil {
900+
return nil
901+
}
902+
903+
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &em.limit); err != nil {
904+
return err
905+
}
906+
for _, fd := range em.fds {
907+
syscall.Close(fd)
908+
}
909+
910+
return nil
911+
}
912+
815913
func TestInjectDevice(t *testing.T) {
816914
type specDirs struct {
817915
etc map[string]string

0 commit comments

Comments
 (0)