Skip to content

runtime: FreeBSD memory corruption involving fork system call #15658

Closed

Description

@derekmarcotte

Please answer these questions before submitting your issue. Thanks!

  1. What version of Go are you using (go version)?

go version go1.6.2 freebsd/amd64

  1. What operating system and processor architecture are you using (go env)?
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="freebsd"
GOOS="freebsd"
GOPATH=""
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/freebsd_amd64"
GO15VENDOREXPERIMENT="1"
CC="cc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="clang++"
  1. What did you do?
package main

/* stdlib includes */
import (
        "fmt"
        "os/exec"
)

func run(done chan struct{}) {
        cmd := exec.Command("true")
        if err := cmd.Start(); err != nil {
                goto finished
        }

        cmd.Wait()

finished:
        done <- struct{}{}
        return
}

func main() {
        fmt.Println("Starting a bunch of goroutines...")

        // 8 & 16 are arbitrary
        done := make(chan struct{}, 16)

        for i := 0; i < 8; i++ {
                go run(done)
        }

        for {
                select {
                case <-done:
                        go run(done)
                }
        }
}
  1. What did you expect to see?

I expect this strange program to spawn instances of /bin/true in parallel, until I stop it.

  1. What did you see instead?

Various types of panics caused by what looks to be corruption within the finalizer lists, caused by what I am assuming is based on race conditions. These panics can happen as quickly as 2 minutes, or much longer. 10 minutes seems a good round number.

Occasionally addspecial gets stuck in an infinite loop holding the lock, and the process wedges. This is illustrated in log 1462933614, with x.next pointing to x. This appears to be corruption of that data structure. I have seen processes in this state run for 22 hours.

I understand there is some trepidation expressed in issue #11485 around the locking of the data structures involved.

Here are some sample messages:

  • fatal error: runtime.SetFinalizer: finalizer already set
  • runtime: nonempty check fails b.log[0]= 0 b.log[1]= 0 b.log[2]= 0 b.log[3]= 0 fatal error: workbuf is empty

1462926841-SetFinalizer-ex1.txt
1462926969-SetFinalizer-ex2.txt
1462933295-nonempty-check-fails.txt
1462933614-wedged.txt

This was run on an 8-core processor, and a 4-core 8-thread processor with ECC RAM, similar results.

Additionally, while this example is an extreme, it also represents the core functionality of a project I've been working on part-time for many months. I'm happy to provide any further assistance diagnosing this issue - I'm very invested!

Activity

  1. derekmarcotte commented on May 12, 2016

    @derekmarcotte
    Author

    Here's another panic experienced in mallocgc by the same sample code:

    1463046620-malloc-panic.txt

  2. added this to the Go1.7Maybe milestone on May 12, 2016
  3. bradfitz commented on May 12, 2016

    @bradfitz
    Contributor

    @derekmarcotte, can you also reproduce this at master? (which will become Go 1.7)

    And do you only see it on FreeBSD, or other operating systems as well?

    /cc @aclements @ianlancetaylor

  4. aclements commented on May 12, 2016

    @aclements
    Member

    @RLH, weren't you seeing "finalizer already set" failures a little while ago? Did you track that down? Could it have been related?

  5. aclements commented on May 12, 2016

    @aclements
    Member

    On closer inspection of the failures you posted (thanks for collecting several, BTW), this smells like memory corruption. Just guessing, there are a few likely culprits. It may be the finalizer code, but I actually think that's less likely. More likely is that it's the fork/exec code: that code is really subtle, mucks with the address space, and contains system-specific parts (which would explain why it's showing up on FreeBSD, but I haven't been able to reproduce it on Linux yet).

    @derekmarcotte, can you try commenting out the runtime.SetFinalizer call in newProcess in os/exec.go (your test doesn't need that finalizer) and see if you can still reproduce it? If you can, that will rule out finalizers.

  6. bradfitz commented on May 12, 2016

    @bradfitz
    Contributor

    Note that FreeBSD runs via gomote, if this is that easily reproducible. I haven't yet tried.

  7. derekmarcotte commented on May 13, 2016

    @derekmarcotte
    Author

    Just got a golang/go dev environment set up on my machine (was from FreeBSD packages). Will report back soon.

  8. derekmarcotte commented on May 13, 2016

    @derekmarcotte
    Author

    Here's the heads of a bunch of logs with the epoch at the start of the process, so you can see the interval. I suspected a race vs. memory corruption because by and large it is the finalizer already set that crashes the process. I thought maybe the gc was setting these as free (or otherwise touching them) before the SetFinalizer had a chance to set their value.

    I didn't include too many of them in my initial report, because I thought they were largely redundant.

    @bradfitz: these logs are against master:

    1463168442
    Starting a bunch of goroutines...
    fatal error: runtime.SetFinalizer: finalizer already set
    
    runtime stack:
    runtime.throw(0x4c366d, 0x2b)
            /home/derek/go/src/github.com/golang/go/src/runtime/panic.go:566 +0x8b fp=0xc420059f48 sp=0xc420059f30
    runtime.SetFinalizer.func2()
            /home/derek/go/src/github.com/golang/go/src/runtime/mfinal.go:375 +0x73 fp=0xc420059f80 sp=0xc420059f48
    runtime.systemstack(0xc420019500)
            /home/derek/go/src/github.com/golang/go/src/runtime/asm_amd64.s:298 +0x79 fp=0xc420059f88 sp=0xc420059f80
    
    
    1463170469
    Starting a bunch of goroutines...
    fatal error: runtime.SetFinalizer: finalizer already set
    
    runtime stack:
    runtime.throw(0x4c366d, 0x2b)
            /home/derek/go/src/github.com/golang/go/src/runtime/panic.go:566 +0x8b fp=0x7fffffffea80 sp=0x7fffffffea68
    runtime.SetFinalizer.func2()
            /home/derek/go/src/github.com/golang/go/src/runtime/mfinal.go:375 +0x73 fp=0x7fffffffeab8 sp=0x7fffffffea80
    runtime.systemstack(0x52ad00)
            /home/derek/go/src/github.com/golang/go/src/runtime/asm_amd64.s:298 +0x79 fp=0x7fffffffeac0 sp=0x7fffffffeab8
    
    
    1463170494
    Starting a bunch of goroutines...
    fatal error: runtime.SetFinalizer: finalizer already set
    
    runtime stack:
    runtime.throw(0x4c366d, 0x2b)
            /home/derek/go/src/github.com/golang/go/src/runtime/panic.go:566 +0x8b fp=0xc420067f48 sp=0xc420067f30
    runtime.SetFinalizer.func2()
            /home/derek/go/src/github.com/golang/go/src/runtime/mfinal.go:375 +0x73 fp=0xc420067f80 sp=0xc420067f48
    runtime.systemstack(0xc420019500)
            /home/derek/go/src/github.com/golang/go/src/runtime/asm_amd64.s:298 +0x79 fp=0xc420067f88 sp=0xc420067f80
    
    
    1463171133
    Starting a bunch of goroutines...
    fatal error: runtime.SetFinalizer: finalizer already set
    
    runtime stack:
    runtime.throw(0x4c366d, 0x2b)
            /home/derek/go/src/github.com/golang/go/src/runtime/panic.go:566 +0x8b fp=0xc4202cbf48 sp=0xc4202cbf30
    runtime.SetFinalizer.func2()
            /home/derek/go/src/github.com/golang/go/src/runtime/mfinal.go:375 +0x73 fp=0xc4202cbf80 sp=0xc4202cbf48
    runtime.systemstack(0xc42001c000)
            /home/derek/go/src/github.com/golang/go/src/runtime/asm_amd64.s:298 +0x79 fp=0xc4202cbf88 sp=0xc4202cbf80
    

    @aclements: I will try your patch next. One variable at a time.

  9. RLH commented on May 14, 2016

    @RLH
    Contributor

    The "fatal error: runtime.SetFinalizer: finalizer already set" bug I was
    seeing a few days ago were on the 1.8 dev.garbage branch and the result of
    a TOC write barrier not marking an object as being published, TOC doing a
    rollback, and reallocating a new object over an old one. Both objects had
    finalizers and the bug was tripped.

    None of the TOC code is in 1.7 or for that matter on TIP. I can force
    myself to imagine that the 1.7 allocCache code could cause a similar
    situation if the allocCache and bitmaps were not coherent. but if that were
    the case it would expect lots of failures all over the place and across all
    platforms, not just freeBSD.

    On Fri, May 13, 2016 at 6:47 PM, Derek Marcotte notifications@github.com
    wrote:

    Here's the heads of a bunch of logs with the epoch at the start of the
    process, so you can see the interval. I suspected a race vs. memory
    corruption because by and large it is the finalizer already set that
    crashes the process. I thought maybe the gc was setting these as free (or
    otherwise touching them) before the SetFinalizer had a chance to set their
    value.

    I didn't include too many of them in my initial report, because I thought
    they were largely redundant.

    @bradfitz https://github.com/bradfitz: these logs are against master:

    1463168442
    Starting a bunch of goroutines...
    fatal error: runtime.SetFinalizer: finalizer already set

    runtime stack:
    runtime.throw(0x4c366d, 0x2b)
    /home/derek/go/src/github.com/golang/go/src/runtime/panic.go:566 +0x8b fp=0xc420059f48 sp=0xc420059f30
    runtime.SetFinalizer.func2()
    /home/derek/go/src/github.com/golang/go/src/runtime/mfinal.go:375 +0x73 fp=0xc420059f80 sp=0xc420059f48
    runtime.systemstack(0xc420019500)
    /home/derek/go/src/github.com/golang/go/src/runtime/asm_amd64.s:298 +0x79 fp=0xc420059f88 sp=0xc420059f80

    1463170469
    Starting a bunch of goroutines...
    fatal error: runtime.SetFinalizer: finalizer already set

    runtime stack:
    runtime.throw(0x4c366d, 0x2b)
    /home/derek/go/src/github.com/golang/go/src/runtime/panic.go:566 +0x8b fp=0x7fffffffea80 sp=0x7fffffffea68
    runtime.SetFinalizer.func2()
    /home/derek/go/src/github.com/golang/go/src/runtime/mfinal.go:375 +0x73 fp=0x7fffffffeab8 sp=0x7fffffffea80
    runtime.systemstack(0x52ad00)
    /home/derek/go/src/github.com/golang/go/src/runtime/asm_amd64.s:298 +0x79 fp=0x7fffffffeac0 sp=0x7fffffffeab8

    1463170494
    Starting a bunch of goroutines...
    fatal error: runtime.SetFinalizer: finalizer already set

    runtime stack:
    runtime.throw(0x4c366d, 0x2b)
    /home/derek/go/src/github.com/golang/go/src/runtime/panic.go:566 +0x8b fp=0xc420067f48 sp=0xc420067f30
    runtime.SetFinalizer.func2()
    /home/derek/go/src/github.com/golang/go/src/runtime/mfinal.go:375 +0x73 fp=0xc420067f80 sp=0xc420067f48
    runtime.systemstack(0xc420019500)
    /home/derek/go/src/github.com/golang/go/src/runtime/asm_amd64.s:298 +0x79 fp=0xc420067f88 sp=0xc420067f80

    1463171133
    Starting a bunch of goroutines...
    fatal error: runtime.SetFinalizer: finalizer already set

    runtime stack:
    runtime.throw(0x4c366d, 0x2b)
    /home/derek/go/src/github.com/golang/go/src/runtime/panic.go:566 +0x8b fp=0xc4202cbf48 sp=0xc4202cbf30
    runtime.SetFinalizer.func2()
    /home/derek/go/src/github.com/golang/go/src/runtime/mfinal.go:375 +0x73 fp=0xc4202cbf80 sp=0xc4202cbf48
    runtime.systemstack(0xc42001c000)
    /home/derek/go/src/github.com/golang/go/src/runtime/asm_amd64.s:298 +0x79 fp=0xc4202cbf88 sp=0xc4202cbf80

    @aclements https://github.com/aclements: I will try your patch next.
    One variable at a time.

    —
    You are receiving this because you were mentioned.
    Reply to this email directly or view it on GitHub
    #15658 (comment)

  10. derekmarcotte commented on May 15, 2016

    @derekmarcotte
    Author

    @aclements: I've run it with your patch, although I haven't been able to babysit it too much.

    The first time, all threads were idle after a number of hours (i.e. 0% cpu across the board). Connecting gdb to that process gave me trouble, and I couldn't get any logging out of it.

    This morning, I was able to connect to a different process that looks a lot like 1462933614-wedged.txt. I've attached a log from gdb there:

    1463270694.txt

    Will keep trying to come up with more info.

  11. derekmarcotte commented on May 16, 2016

    @derekmarcotte
    Author

    @aclements: Here's some more logs from a binary build with the patch:

    1463315708-finalizer-already-set.txt
    1463349807-finalizer-already-set.txt
    1463352601-workbuf-not-empty.txt
    1463362849-workbuf-empty.txt
    1463378745-wedged-gdb.txt

    Please let me know if I can be of further assistance?

  12. aclements commented on May 16, 2016

    @aclements
    Member

    Thanks for the logs! I hadn't realized there were two finalizers involved here. Could you also comment out the SetFinalizer in NewFile in os/file_unix.go and see if it's still reproducible? (Your test also doesn't need that finalizer.)

  13. aclements commented on May 16, 2016

    @aclements
    Member

    I suspected a race vs. memory corruption because by and large it is the finalizer already set that crashes the process.

    I didn't mean to say that it isn't necessarily a race. It's actually quite likely a race, but it's resulting in corruption of internal runtime structures, which suggests that the race is happening on freed memory. The "workbuf is empty" failure mode especially points at memory corruption, which is why my initial guess is that the finalizers (and the specials queue in general) may be victims rather than perpetrators. It's also easy to get finalizers out of the picture, while it's harder to get fork/exec out of the picture without completely changing the program. :)

  14. derekmarcotte commented on May 17, 2016

    @derekmarcotte
    Author

    Thanks @aclements !

    One crash, ~4 hours into running, since removing the SetFinalizer in NewFile.

    1463429459.txt

    I have a second process running for almost 11 hours, with 4 of the threads wedged, but it is still doing work.

  15. 252 remaining items

  16. modified the milestones: Go1.11, Go1.12 on Jul 6, 2018
  17. mattmacy commented on Jul 6, 2018

    @mattmacy

    @stevenh is there any correlation with the amount of memory in the system or the number of cores? For example, would lowering hw.physmem on a 48-way help?

  18. stevenh commented on Jul 7, 2018

    @stevenh
    Contributor
  19. stevenh commented on Nov 28, 2018

    @stevenh
    Contributor

    I've been running the usual tests on 11.2-RELEASE-p3 with go1.11.1 and have not managed to reproduce after a weeks run.

    Has anyone else seen a crash with these updated versions?

  20. modified the milestones: Go1.12, Go1.13 on Dec 18, 2018
  21. stevenh commented on Dec 21, 2018

    @stevenh
    Contributor

    Ok still no issues triggered on my test box and its been running forks in a loop since 18th and 20th of November

    root  50215  273.0  0.0   104384   4988  9  S+J  20Nov18  134201:59.00 ./test
    root  46993  259.4  0.0   104640   3204  8  S+J  18Nov18  151748:35.09 ./test2
    

    Given this I'm tempted to say that the issue has been fixed either in FreeBSD or golang runtime.

  22. ianlancetaylor commented on Dec 21, 2018

    @ianlancetaylor
    Contributor

    Thanks very much for following up on this., I'm going to close the issues in the hopes that it is indeed fixed. Anyone, please comment if you find future failures.

  23. stevenh commented on Jan 3, 2019

    @stevenh
    Contributor

    While updating our test box to 12.0 from 11.2 I noticed that this latest test was running with a potentially relevant kernel patch on top of 11.2-RELEASE.

    If anyone can reproduce this issue on 11.2 then they should try applying the following to see if was indeed the fix:
    https://svnweb.freebsd.org/base?view=revision&revision=335171

    This is already included in 12.0-RELEASE and was MFC'ed to 11-stable as of:
    https://svnweb.freebsd.org/base?view=revision&revision=335508

  24. locked and limited conversation to collaborators on Jan 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions