@@ -21,9 +21,11 @@ import (
21
21
"fmt"
22
22
"os"
23
23
"path/filepath"
24
+ "runtime"
24
25
"strconv"
25
26
"strings"
26
27
"sync"
28
+ "syscall"
27
29
"testing"
28
30
"time"
29
31
@@ -812,6 +814,102 @@ devices:
812
814
}
813
815
}
814
816
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
+
815
913
func TestInjectDevice (t * testing.T ) {
816
914
type specDirs struct {
817
915
etc map [string ]string
0 commit comments