src/io/fs/glob.go | 14 ++++++++++++-- src/io/fs/glob_test.go | 10 ++++++++++ diff --git a/src/io/fs/glob.go b/src/io/fs/glob.go index 45d9cb61b9632af83bf9834ef99d47e6133b193b..0e529cd05d139e20e639a97cc5caa6cc440be033 100644 --- a/src/io/fs/glob.go +++ b/src/io/fs/glob.go @@ -31,6 +31,16 @@ // If fs implements GlobFS, Glob calls fs.Glob. // Otherwise, Glob uses ReadDir to traverse the directory tree // and look for matches for the pattern. func Glob(fsys FS, pattern string) (matches []string, err error) { + return globWithLimit(fsys, pattern, 0) +} + +func globWithLimit(fsys FS, pattern string, depth int) (matches []string, err error) { + // This limit is added to prevent stack exhaustion issues. See + // CVE-2022-30630. + const pathSeparatorsLimit = 10000 + if depth > pathSeparatorsLimit { + return nil, path.ErrBadPattern + } if fsys, ok := fsys.(GlobFS); ok { return fsys.Glob(pattern) } @@ -59,9 +69,9 @@ return nil, path.ErrBadPattern } var m []string - m, err = Glob(fsys, dir) + m, err = globWithLimit(fsys, dir, depth+1) if err != nil { - return + return nil, err } for _, d := range m { matches, err = glob(fsys, d, file, matches) diff --git a/src/io/fs/glob_test.go b/src/io/fs/glob_test.go index f19bebed77f6c7c7e9fad7017b94b9c557a77bca..d052eab371366f00a17692774c87fec260b89946 100644 --- a/src/io/fs/glob_test.go +++ b/src/io/fs/glob_test.go @@ -8,6 +8,7 @@ import ( . "io/fs" "os" "path" + "strings" "testing" ) @@ -52,6 +53,15 @@ _, err := Glob(os.DirFS("."), pattern) if err != path.ErrBadPattern { t.Errorf("Glob(fs, %#q) returned err=%v, want path.ErrBadPattern", pattern, err) } + } +} + +func TestCVE202230630(t *testing.T) { + // Prior to CVE-2022-30630, a stack exhaustion would occur given a large + // number of separators. There is now a limit of 10,000. + _, err := Glob(os.DirFS("."), "/*"+strings.Repeat("/", 10001)) + if err != path.ErrBadPattern { + t.Fatalf("Glob returned err=%v, want %v", err, path.ErrBadPattern) } }