UnkindPartition / tasty

Modern and extensible testing framework for Haskell
640 stars 110 forks source link

reproducible segfault when running tasty #326

Closed joeyh closed 7 months ago

joeyh commented 2 years ago

main.hs.txt I have a segfault that occurs when tasty is running. The attached test case is as minimized from the original as I was able to get it. It segfaults at least 1 time out of 10 that it's run.

Note that uncommenting the "-- othertests," line makes it never segfault. I used essentially this workaround in my original program as well.

Deleting some of the environment variable setting seems to avoid the segfault.

I have reproduced the segfault on 2 machines. Both have ghc 8.8.4 and tasty-1.2.3, and are running Debian unstable.

I can also reproduce it using stack, with a more recent version of tasty, and pinned versions:

stack --resolver lts-18.28 runghc --package tasty --package tasty-hunit --package tasty-rerun main.hs

Of course this may be a ghc bug. I can forward to ghc if you're convinced that tasty does not do anything memory unsafe that could cause a segfault.

But often segfaults in the middle of the print "setTestMode", as seen in the following series of runs, which makes me suspect that withResource is somehow involved in the problem:

joey@darkstar:~>for x in $(seq 1 10); do runghc main; done
"setTestMode"
Tests
  Unit Tests
    add dup: OK

All 1 tests passed (0.00s)
True
"setTeSegmentation fault
"setTestMode"
Tests
  Unit Tests
    add dup: OK

All 1 tests passed (0.00s)
True

"setTestMSegmentation fault
"setTestMoSegmentation fault
"setTestMoSegmentation fault
"setTestModSegmentation fault
"setTestMoSegmentation fault
"setTestModSegmentation fault
"setTesSegmentation fault
Bodigrim commented 2 years ago

FWIW I cannot reproduce it with stack --resolver lts-18.28 on macOS, seems working fine.

joeyh commented 2 years ago

Adding threadDelay 1 to setTestMode avoids the segfault.

Also, when it does not segfault, setTestMode output is sometimes interleaved with tasty output, like this:

"setTestM[d?e2"5
lTests

"setTestMode
?25lTests

With the threadDelay 1, that interleaving does not happen.

This makes me suspect some kind of race condition.

joeyh commented 2 years ago

I've been doing some printf debugging in tasty, and some interesting things I've found:

The segfault happens either in the line res <- doInit, or immediately after. I added a print "OUT" >> hFlush stdout after that line, and it sometimes crashed while displaying that, eg "OUTSegmentation fault

Adding threadDelay 1 before k0 smap avoids the crash. So it seems that the k0 callback is somehow involved in the crash.

joeyh commented 2 years ago

I also saw an infinite loop of repeated errors "lost signal due to full pipe: 11" when I modified my program to run installSignalHandlers. I have not seen that with the test case so far, but that's also a segfault.

That message pointed me to this ghc bug, involving another tasty test suite: https://gitlab.haskell.org/ghc/ghc/-/issues/15544

That ghc bug is supposedly fixed in 8.8.4, so I'll followup by reopening or opening a new bug since it seems the fix didn't entirely work, and this is indeed a ghc bug.

joeyh commented 2 years ago

I filed this ghc issue https://gitlab.haskell.org/ghc/ghc/-/issues/21243

Matthew Pickering kindly did some analysis, that the segfault is due to non-threadsafe mixing of getEnv and setEnv. My test case does setEnv, but it does not getEnv. The backtrace shows that the getEnv is from hSupportsANSI, which tasty does call.

Bodigrim commented 7 months ago

ansi-terminal-1.1.1 should now call getEnv less, and I raised an MR to document the situation (https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12497), but otherwise there is not much to fix in tasty. Closing.