Skip to content

runtime: windows/386: suspending a thread inside NtContinue crashes recovery from a fault #81943

Open

Description

@lizthegrey

Go version

go1.27.1 and master (be1160f), windows/386.

What operating system and processor architecture are you using?

Windows Server 2025 (WoW64). Intel Xeon Platinum 8488C (AWS m7i); also reproduced, with the CL 828906 guard applied, on AMD EPYC 7R13 (m6a).

What did you do?

Built this program with GOARCH=386 and ran it on 64-bit Windows with GODEBUG=asyncpreemptoff=1. Without that setting, #79249 / #71818 usually crashes or hangs first; see the notes.

package main

import (
	"fmt"
	"io"
	"runtime"
	"runtime/pprof"
	"sync"
	"time"
)

func fault() {
	defer func() { recover() }()
	var p *int
	*p = 1
}

func main() {
	// Profile at 1 kHz so threads are suspended often. (StartCPUProfile
	// then warns that the rate is already set; that is expected.)
	runtime.SetCPUProfileRate(1000)
	pprof.StartCPUProfile(io.Discard)

	go func() {
		for {
			runtime.GC()
		}
	}()

	for start := time.Now(); time.Since(start) < time.Minute; {
		var wg sync.WaitGroup
		for i := 0; i < 8; i++ {
			wg.Add(1)
			go func() {
				defer wg.Done()
				for j := 0; j < 50; j++ {
					fault()
				}
			}()
		}
		wg.Wait()
	}
	fmt.Println("OK")
}

What did you see happen?

Most runs crash within seconds:

Exception 0x0 0xc0000005 0x0 0x7771b7ba
PC=0x7771b7ba
windows/386, GODEBUG=asyncpreemptoff=1 crashed runs
go1.27.1 68/73
master (be1160f) 27/29

The PC is KiUserExceptionDispatcher+0x4a, right after call RtlRaiseException. ntdll only reaches that code when ZwContinue returns. The dispatcher then raises a noncontinuable exception whose code is the returned status (0), chained to the original 0xc0000005 record.

With async preemption off, nothing is ever injected: the CPU profiler only suspends threads and reads their context. In an instrumented runtime's WER dump, the crashing thread was last seen with CONTEXT_SERVICE_ACTIVE at ZwContinue+0xc, i.e. suspended inside NtContinue. The context it was resuming (Eip=sigresume) was valid.

What did you expect to see?

OK: each fault recovers through sigpanic.

Notes

  • Scope: windows/386 under WoW64. Native windows/amd64 (EPYC 7R13) with the same program had no crashes in 90 runs (over 10⁹ recovered faults). Any 386 program that recovers from faults while the CPU profiler runs, or while the GC preempts it, can crash. The GC alone hits it, just less often.
  • https://go.dev/cl/828906 (runtime: intermittent startup panics across multiple Go applications on Windows 11 #79249) unmasks it. On stock Go with async preemption on, runtime: intermittent startup panics across multiple Go applications on Windows 11 #79249 usually fails first. With that CL, the same program, without the GODEBUG setting, crashed 140 of 141 runs with this signature.
  • Likely mechanism (not verified): WoW64's NtContinue restores the full 32-bit context on its way back, and an external suspend or context read in that window loses it, so NtContinue takes the normal syscall return with eax=0.
  • Fix (CL to follow): keep preemptM and profileLoop from suspending a thread between Go's exception handler starting and sigpanic running. A per-M flag with a small handshake does this, and the M stays non-preemptible until sigpanic. On top of CL 828906's guard, it brought this crash from 56/96 runs to 0/65 on Intel and AMD. This overlaps with go.dev/cl/823085 (@rhysh), which refactors the same suspension code.

Related: #71818, #79249, #72739.

Activity

  1. dmitshur commented on Oct 5, 2026

    @dmitshur
    Member

    CC @golang/windows, @golang/runtime.

  2. added this to the Backlog milestone on Oct 5, 2026
  3. added
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    on Oct 5, 2026
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

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.OS-Windowscompiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions