tinygo icon indicating copy to clipboard operation
tinygo copied to clipboard

io/fs test fails with Go 1.18.4

Open QuLogic opened this issue 3 years ago • 2 comments
trafficstars

It appears that all CI that uses Go 1.18 is still pinned on 1.18.1, but with 1.18.4, there is a new failure that did not occur in 1.18.3, with make tinygo-test. However, it doesn't appear that -v does anything, so i can't get much more info than FAIL io/fs 2.363s.

Looking at the diff for 1.18.3 to 1.18.4, I think the failure is the new CVE-related test.

It fails on both 0.24.0 and 0.23.0.

QuLogic avatar Jul 21 '22 04:07 QuLogic

Using -run TestCVE202230630, I can confirm that it is the new CVE-related test, which could mean that tinygo-built programs may be vulnerable to CVE-2022-30630. But I'm not sure why, as there does not appear to be a Glob implementation anywhere in tinygo.

QuLogic avatar Jul 21 '22 05:07 QuLogic

Extracting the test into a separate program:

package main

import (
        "io/fs"
        "log"
        "os"
        "strconv"
        "strings"
)

func main() {
        count := 10001
        if len(os.Args) > 1 {
                var err error
                count, err = strconv.Atoi(os.Args[1])
                if err != nil {
                        log.Fatalf("Failed to parse count: %v", err)
                }
        }

        // 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 := fs.Glob(os.DirFS("."), "/*"+strings.Repeat("/", count))
        log.Fatalf("Glob returned err=%v", err)
}

then shows a segfault in gdb

#0  0x000000000020ee02 in path.matchChunk (chunk=..., s=...)
        failed = <optimized out>
        r = <optimized out>
        n = <optimized out>
        negated = <optimized out>
        n = <optimized out>
        match = <optimized out>
        nrange = <optimized out>
        lo = <optimized out>
        hi = <optimized out>
        err = <optimized out>
        ErrBadPattern = <optimized out>
#1  0x000000000020ebf5 in path.Match (pattern=..., name=...) at /usr/lib/golang/src/path/match.go:49
        star = false
        chunk = {ptr = 0x7fffb800b960 "/*", '/' <repeats 198 times>..., len = 1}
        err = <optimized out>
        t = <optimized out>
        i = <optimized out>
        t = <optimized out>
        err = <optimized out>
        err = <optimized out>
        ok = <optimized out>
        ok = <optimized out>
#2  0x000000000021727a in io/fs.globWithLimit (fsys=..., pattern=..., depth=0) at /usr/lib/golang/src/io/fs/glob.go:49
        ok = false
        err = <optimized out>
        dir = <optimized out>
        m = <optimized out>
        err = <optimized out>
        matches = <optimized out>
        fsys = <optimized out>
        file = <optimized out>
        d = <optimized out>
...
#273 0x000000000021150f in io/fs.Glob (pattern=..., fsys=...) at /usr/lib/golang/src/io/fs/glob.go:34
No locals.
#274 main.main () at /builddir/build/BUILD/tinygo-0.24.0/check.go:14
        err = <optimized out>
#275 runtime.run$1 () at /builddir/build/BUILDROOT/tinygo-0.24.0-1.fc37.x86_64/usr/lib64/tinygo/src/runtime/scheduler_any.go:25
        schedulerDone = <optimized out>
#276 <goroutine wrapper> () at /builddir/build/BUILDROOT/tinygo-0.24.0-1.fc37.x86_64/usr/lib64/tinygo/src/runtime/scheduler_any.go:23
No locals.
#277 0x000000000020c8fe in tinygo_startTask () at /builddir/build/BUILDROOT/tinygo-0.24.0-1.fc37.x86_64/usr/lib64/tinygo/src/internal/task/task_stack_amd64.S:29
No locals.

Playing around with the count shows that it works for 268, but crashes on 269. Is it simply running out of stack space due to the recursive io/fs.globWithLimit?

QuLogic avatar Jul 21 '22 05:07 QuLogic

Using -run TestCVE202230630, I can confirm that it is the new CVE-related test, which could mean that tinygo-built programs may be vulnerable to CVE-2022-30630. But I'm not sure why, as there does not appear to be a Glob implementation anywhere in tinygo.

Most of the standard library packages (including glob) are from the Go installation so updating the Go toolchain should also fix this specific bug for TinyGo.

Playing around with the count shows that it works for 268, but crashes on 269. Is it simply running out of stack space due to the recursive io/fs.globWithLimit?

Probably. While the Go runtime normally uses a stack of 1GB or so (they start out small and grow on demand), TinyGo will use a relatively small stack by default. Therefore, it can in fact run out of stack space, and corrupt memory when this happens. (I'd like to move to threads in the future but for now there is no check on stack overflows like OS threads normally have).

aykevl avatar Aug 21 '22 21:08 aykevl

This was fixed by #3143.

QuLogic avatar Oct 08 '22 05:10 QuLogic