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

Go 1.21.0

Does this issue reproduce with the latest release?

Haven't yet tried Go 1.21.1.

What operating system and processor architecture are you using (go env)?

plan9/amd64

What did you do?

A Plan 9 user (@9nut) sent me this crash trying to run Tailscale (go install tailscale.com/cmd/tailscaled at https://github.com/tailscale/tailscale/commit/6fd1961cd70385568ac02c69a60d4f48314bd767). I don't have enough Plan 9 knowledge to debug or repro:

term% ./tailscaled --statedir $home/lib/tailscale
logtail started
Program starting: v1.49.0-dev20230830-tae747a2e4-dirty, Go 1.21.0: []string{"./tailscaled", "--statedir", "/usr/glenda/lib/tailscale"}
LogID: c07ea905679a33eba472be24f4634bb25d3f96ef1d7bb6fe2600cf86dd17c90a
logpolicy: using UserCacheDir, "/usr/glenda/lib/cache/Tailscale"
runtime: bad pointer in frame tailscale.com/logtail.(*Logger).Write at 0x480f59c0: 0x42
fatal error: invalid pointer found on stack

runtime stack:
runtime.throw({0xac1578?, 0x105cda?})
    /Users/fst/go/src/runtime/panic.go:1077 +0x65 fp=0x7fffffffe930 sp=0x7fffffffe900 pc=0x237885
runtime.adjustpointers(0x7fffffffebb8?, 0x7fffffffea00, 0x0?, {0x0?, 0x0?})
    /Users/fst/go/src/runtime/stack.go:627 +0x205 fp=0x7fffffffe9a0 sp=0x7fffffffe930 pc=0x24d6a5
runtime.adjustframe(0x7fffffffebb8, 0x7fffffffea98)
    /Users/fst/go/src/runtime/stack.go:684 +0xdb fp=0x7fffffffea30 sp=0x7fffffffe9a0 pc=0x24d7db
runtime.copystack(0x4817e680, 0x800000002?)
    /Users/fst/go/src/runtime/stack.go:935 +0x2ae fp=0x7fffffffed28 sp=0x7fffffffea30 pc=0x24df6e
runtime.newstack()
    /Users/fst/go/src/runtime/stack.go:1116 +0x7e7 fp=0x7fffffffeed0 sp=0x7fffffffed28 pc=0x24e887
runtime.morestack()
    /Users/fst/go/src/runtime/asm_amd64.s:593 +0x93 fp=0x7fffffffeed8 sp=0x7fffffffeed0 pc=0x2669d3

goroutine 9 [copystack]:
fmt.(*pp).printArg(0x4807e820?, {0x955f20?, 0x4801a498?}, 0x73?)
    /Users/fst/go/src/fmt/print.go:681 +0x6f7 fp=0x480f5540 sp=0x480f5538 pc=0x300db7
fmt.(*pp).doPrintf(0x4807e820, {0xaa9d03, 0x2}, {0x480f58d8?, 0x1, 0x1})
    /Users/fst/go/src/fmt/print.go:1077 +0x39e fp=0x480f5638 sp=0x480f5540 pc=0x3037fe
fmt.Appendf({0x481ae000, 0x0, 0x90}, {0xaa9d03, 0x2}, {0x4806f8d8, 0x1, 0x1})
    /Users/fst/go/src/fmt/print.go:249 +0x7a fp=0x480f5698 sp=0x480f5638 pc=0x2fdaba
tailscale.com/logpolicy.logWriter.Write.(*Logger).Printf.func1({0x481ae000?, 0x4801a3c0?, 0x0?})
    /Users/fst/go/src/log/log.go:269 +0x2c fp=0x480f56e8 sp=0x480f5698 pc=0x6de14c
log.(*Logger).output(0x48118750, 0x0, 0x90?, 0x4806f8e8)
    /Users/fst/go/src/log/log.go:238 +0x36a fp=0x480f58b0 sp=0x480f56e8 pc=0x424cea
log.(*Logger).Printf(...)
    /Users/fst/go/src/log/log.go:268
tailscale.com/logpolicy.logWriter.Write({0x481ae090?}, {0x481ae090?, 0x3c, 0x480469b0?})
    /Users/fst/src/tailscale/logpolicy/logpolicy.go:195 +0xba fp=0x480f5928 sp=0x480f58b0 pc=0x6de0da
tailscale.com/logtail.(*Logger).Write(0x480bafc0, {0x481ae090?, 0x0?, 0x0?})
    /Users/fst/src/tailscale/logtail/logtail.go:741 +0x16b fp=0x480f59d8 sp=0x480f5928 pc=0x5e49ab
log.(*Logger).output(0x4803e3f0, 0x0, 0x4e3485?, 0x4806fbc0)
    /Users/fst/go/src/log/log.go:245 +0x483 fp=0x480f5ba0 sp=0x480f59d8 pc=0x424e03
log.Printf({0xacb8cc?, 0x48119080?}, {0x480469b0?, 0x1264420?, 0x10?})
    /Users/fst/go/src/log/log.go:397 +0x6d fp=0x480f5c00 sp=0x480f5ba0 pc=0x42504d
tailscale.com/types/logger.RateLimitedFnWithClock.func1({0xacb8cc, 0x27}, {0x480469b0, 0x1, 0x1})
    /Users/fst/src/tailscale/types/logger/logger.go:220 +0x7db fp=0x480f5cd8 sp=0x480f5c00 pc=0x4e24bb
main.createEngine(0x481239a0, 0x4807da40?)
    /Users/fst/src/tailscale/cmd/tailscaled/tailscaled.go:571 +0x17e fp=0x480f5d90 sp=0x480f5cd8 pc=0x8f641e
main.getLocalBackend({0x0?, 0x0?}, 0x481239a0, {0xc0, 0x7e, 0xa9, 0x5, 0x67, 0x9a, 0x33, ...}, ...)
    /Users/fst/src/tailscale/cmd/tailscaled/tailscaled.go:481 +0x11a fp=0x480f5ed0 sp=0x480f5d90 pc=0x8f4afa
main.startIPNServer.func2()
    /Users/fst/src/tailscale/cmd/tailscaled/tailscaled.go:447 +0x28b fp=0x480f5fe0 sp=0x480f5ed0 pc=0x8f476b
runtime.goexit()
    /Users/fst/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0x480f5fe8 sp=0x480f5fe0 pc=0x268701
created by main.startIPNServer in goroutine 1
    /Users/fst/src/tailscale/cmd/tailscaled/tailscaled.go:435 +0x4c5

goroutine 1 [runnable]:
context.WithValue({0xbe5ad8?, 0x481239f0?}, {0x9856a0?, 0x1250ca0?}, {0xa84a60?, 0x481ba000?})
    /Users/fst/go/src/context/context.go:708 +0x192 fp=0x480f3a88 sp=0x480f3a80 pc=0x28ca72
net/http.(*Server).Serve(0x481ba000, {0xbe49a0, 0x48048400})
    /Users/fst/go/src/net/http/server.go:3054 +0x30b fp=0x480f3bb8 sp=0x480f3a88 pc=0x4a1feb
tailscale.com/ipn/ipnserver.(*Server).Run(0x4804e900, {0xbe5ad8?, 0x481239f0}, {0xbe49a0?, 0x48048400})
    /Users/fst/src/tailscale/ipn/ipnserver/server.go:521 +0x3bf fp=0x480f3c80 sp=0x480f3bb8 pc=0x8694df
main.startIPNServer({0xbe5a30, 0x12956a0}, 0x481239a0, {0xc0, 0x7e, 0xa9, 0x5, 0x67, 0x9a, 0x33, ...}, ...)
    /Users/fst/src/tailscale/cmd/tailscaled/tailscaled.go:457 +0x4e6 fp=0x480f3d78 sp=0x480f3c80 pc=0x8f4286
main.run()
    /Users/fst/src/tailscale/cmd/tailscaled/tailscaled.go:396 +0x49f fp=0x480f3e90 sp=0x480f3d78 pc=0x8f393f
main.main()
    /Users/fst/src/tailscale/cmd/tailscaled/tailscaled.go:239 +0x8dd fp=0x480f3f38 sp=0x480f3e90 pc=0x8f31bd
runtime.main()
    /Users/fst/go/src/runtime/proc.go:267 +0x2fb fp=0x480f3fe0 sp=0x480f3f38 pc=0x239e7b
runtime.goexit()
    /Users/fst/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0x480f3fe8 sp=0x480f3fe0 pc=0x268701

goroutine 2 [force gc (idle)]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
    /Users/fst/go/src/runtime/proc.go:398 +0xce fp=0x48071fa0 sp=0x48071f80 pc=0x23a2ee
runtime.goparkunlock(...)
    /Users/fst/go/src/runtime/proc.go:404
runtime.forcegchelper()
    /Users/fst/go/src/runtime/proc.go:322 +0xa7 fp=0x48071fe0 sp=0x48071fa0 pc=0x23a127
runtime.goexit()
    /Users/fst/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0x48071fe8 sp=0x48071fe0 pc=0x268701
created by runtime.init.6 in goroutine 1
    /Users/fst/go/src/runtime/proc.go:310 +0x1a

goroutine 3 [GC sweep wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
    /Users/fst/go/src/runtime/proc.go:398 +0xce fp=0x48072f78 sp=0x48072f58 pc=0x23a2ee
runtime.goparkunlock(...)
    /Users/fst/go/src/runtime/proc.go:404
runtime.bgsweep(0x48052150?)
    /Users/fst/go/src/runtime/mgcsweep.go:280 +0x94 fp=0x48072fc8 sp=0x48072f78 pc=0x2250b4
runtime.gcenable.func1()
    /Users/fst/go/src/runtime/mgc.go:200 +0x25 fp=0x48072fe0 sp=0x48072fc8 pc=0x219fc5
runtime.goexit()
    /Users/fst/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0x48072fe8 sp=0x48072fe0 pc=0x268701
created by runtime.gcenable in goroutine 1
    /Users/fst/go/src/runtime/mgc.go:200 +0x66

goroutine 4 [GC scavenge wait]:
runtime.gopark(0x48052150?, 0xbd70b0?, 0x1?, 0x0?, 0x48006d00?)
    /Users/fst/go/src/runtime/proc.go:398 +0xce fp=0x48073f70 sp=0x48073f50 pc=0x23a2ee
runtime.goparkunlock(...)
    /Users/fst/go/src/runtime/proc.go:404
runtime.(*scavengerState).park(0x12646c0)
    /Users/fst/go/src/runtime/mgcscavenge.go:425 +0x49 fp=0x48073fa0 sp=0x48073f70 pc=0x222909
runtime.bgscavenge(0x0?)
    /Users/fst/go/src/runtime/mgcscavenge.go:653 +0x3c fp=0x48073fc8 sp=0x48073fa0 pc=0x222efc
runtime.gcenable.func2()
    /Users/fst/go/src/runtime/mgc.go:201 +0x25 fp=0x48073fe0 sp=0x48073fc8 pc=0x219f65
runtime.goexit()
    /Users/fst/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0x48073fe8 sp=0x48073fe0 pc=0x268701
created by runtime.gcenable in goroutine 1
    /Users/fst/go/src/runtime/mgc.go:201 +0xa5

goroutine 5 [finalizer wait]:
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
    /Users/fst/go/src/runtime/proc.go:398 +0xce fp=0x4806ce28 sp=0x4806ce08 pc=0x23a2ee
runtime.runfinq()
    /Users/fst/go/src/runtime/mfinal.go:193 +0x107 fp=0x4806cfe0 sp=0x4806ce28 pc=0x219027
runtime.goexit()
    /Users/fst/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0x4806cfe8 sp=0x4806cfe0 pc=0x268701
created by runtime.createfing in goroutine 1
    /Users/fst/go/src/runtime/mfinal.go:163 +0x3d

goroutine 6 [select]:
runtime.gopark(0x48070e08?, 0x2?, 0x2?, 0x0?, 0x48070dd4?)
    /Users/fst/go/src/runtime/proc.go:398 +0xce fp=0x48070c40 sp=0x48070c20 pc=0x23a2ee
runtime.selectgo(0x48070e08, 0x48070dd0, 0xd4?, 0x0, 0x1201ec0?, 0x1)
    /Users/fst/go/src/runtime/select.go:327 +0x725 fp=0x48070d60 sp=0x48070c40 pc=0x249ce5
tailscale.com/logtail.(*Logger).drainBlock(...)
    /Users/fst/src/tailscale/logtail/logtail.go:287
tailscale.com/logtail.(*Logger).drainPending(0x480bafc0, {0x48041000?, 0x1000?, 0x1296ac0?})
    /Users/fst/src/tailscale/logtail/logtail.go:317 +0x1c5 fp=0x48070e60 sp=0x48070d60 pc=0x5e1c85
tailscale.com/logtail.(*Logger).uploading(0x480bafc0, {0xbe5ad8, 0x48123900})
    /Users/fst/src/tailscale/logtail/logtail.go:361 +0xcf fp=0x48070fb8 sp=0x48070e60 pc=0x5e20af
tailscale.com/logtail.NewLogger.func3()
    /Users/fst/src/tailscale/logtail/logtail.go:163 +0x28 fp=0x48070fe0 sp=0x48070fb8 pc=0x5e1768
runtime.goexit()
    /Users/fst/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0x48070fe8 sp=0x48070fe0 pc=0x268701
created by tailscale.com/logtail.NewLogger in goroutine 1
    /Users/fst/src/tailscale/logtail/logtail.go:163 +0xa0c

goroutine 7 [syscall]:
runtime.notetsleepg(0x129b8e0?, 0x0?)
    /Users/fst/go/src/runtime/lock_sema.go:294 +0x29 fp=0x4806dfa0 sp=0x4806df58 pc=0x20b789
os/signal.signal_recv()
    /Users/fst/go/src/runtime/sigqueue_plan9.go:110 +0x52 fp=0x4806dfc0 sp=0x4806dfa0 pc=0x264d92
os/signal.loop()
    /Users/fst/go/src/os/signal/signal_plan9.go:27 +0x13 fp=0x4806dfe0 sp=0x4806dfc0 pc=0x852193
runtime.goexit()
    /Users/fst/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0x4806dfe8 sp=0x4806dfe0 pc=0x268701
created by os/signal.Notify.func1.1 in goroutine 1
    /Users/fst/go/src/os/signal/signal.go:151 +0x1f

goroutine 8 [select]:
runtime.gopark(0x4806efb0?, 0x2?, 0x0?, 0x0?, 0x4806ef7c?)
    /Users/fst/go/src/runtime/proc.go:398 +0xce fp=0x4806ee20 sp=0x4806ee00 pc=0x23a2ee
runtime.selectgo(0x4806efb0, 0x4806ef78, 0x0?, 0x0, 0x0?, 0x1)
    /Users/fst/go/src/runtime/select.go:327 +0x725 fp=0x4806ef40 sp=0x4806ee20 pc=0x249ce5
main.startIPNServer.func1()
    /Users/fst/src/tailscale/cmd/tailscaled/tailscaled.go:420 +0xbc fp=0x4806efe0 sp=0x4806ef40 pc=0x8f493c
runtime.goexit()
    /Users/fst/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0x4806efe8 sp=0x4806efe0 pc=0x268701
created by main.startIPNServer in goroutine 1
    /Users/fst/src/tailscale/cmd/tailscaled/tailscaled.go:419 +0x2a9

goroutine 10 [select]:
runtime.gopark(0x481b6fb0?, 0x2?, 0x0?, 0x0?, 0x481b6f9c?)
    /Users/fst/go/src/runtime/proc.go:398 +0xce fp=0x481b6e40 sp=0x481b6e20 pc=0x23a2ee
runtime.selectgo(0x481b6fb0, 0x481b6f98, 0x0?, 0x0, 0x0?, 0x1)
    /Users/fst/go/src/runtime/select.go:327 +0x725 fp=0x481b6f60 sp=0x481b6e40 pc=0x249ce5
tailscale.com/ipn/ipnserver.(*Server).Run.func2()
    /Users/fst/src/tailscale/ipn/ipnserver/server.go:491 +0x8c fp=0x481b6fe0 sp=0x481b6f60 pc=0x8698ac
runtime.goexit()
    /Users/fst/go/src/runtime/asm_amd64.s:1650 +0x1 fp=0x481b6fe8 sp=0x481b6fe0 pc=0x268701
created by tailscale.com/ipn/ipnserver.(*Server).Run in goroutine 1
    /Users/fst/src/tailscale/ipn/ipnserver/server.go:490 +0x1be
term% 
term% 

What did you expect to see?

Not a crash in runtime.copystack.

What did you see instead?

A crash.

Comment From: randall77

The offending stack slot is SP+0x98 in tailscale.com/logtail.(*Logger).Write(SB). It is a pointer slot, and is initialized here:

  logtail.go:734        0x5e76a2                66440fd6bc2498000000            MOVQ X15, 0x98(SP)                                              

If X15 wasn't properly initialized to 0, that might be the issue. I recall plan9 has some strange interactions with floating-point (can't be used in signal handlers?). This could also be the result of some plan9 assembly that doesn't properly zero X15 on transitions from abi0 to abiInternal (which would be in assembly somewhere).

The other write to SP+0x98 can't be the problem, as it is of a known stack pointer.

  logtail.go:751        0x5e7888                4c8d842488000000                LEAQ 0x88(SP), R8                                               
  logtail.go:751        0x5e7890                4c89842498000000                MOVQ R8, 0x98(SP)                                               

Comment From: cherrymui

We should already don't use X15 for zeroing on Plan 9. The rewriting rules for MOVOstoreconst and DUFFZERO are guarded with useSSE and !noDuffDevice, which are false on Plan 9. Hmmm, https://cs.opensource.google/go/go/+/master:src/cmd/compile/internal/amd64/ggen.go;l=66 this one is not guarded. Maybe this is the problem.

Comment From: cherrymui

Looks like CL https://go.dev/cl/453536 is the culprit. It added a new use of X15 without the guard.

Comment From: bradfitz

@cherrymui, nice find! I look forward to what a test for this will look like 😅

Comment From: cherrymui

For a test, I guess we could have an assembly function that clobbers X15, then call a function with a certain frame layout which would trigger the zeroing code, then check it is actually zeroed. The ABI wrapper between assembly and Go would zero X15, but I think we don't do that on Plan 9, so we can keep X15 clobbered.

Maybe we could have the compiler just error out if X15 (or any SSE) is used on Plan 9 in compiled code? (We should probably want to allow assembly code.)

Comment From: rminnich

I am wondering, since you are using SSE: should you be testing for buildcfg.GOAMD64 > 1, not isPlan9? I got burned at google by SSE when GOAMD64 got defaulted to v2 and I ran on an amd64 machine that did not have SSE enabled. We fixed the glitch, but added GOAMD64=v1 to the build environment. Is this a similar case? Using x15 without checking GOAMD64 seems dicey to me.

Comment From: randall77

@rminnich I'm not sure what you are referring to - maybe 386? Because our amd64 architecture port requires SSE, and has since Go was released.

Comment From: mdempsky

Removing release blocker since Plan 9 isn't a first class port.

Comment From: 9nut

@bradfitz @cherrymui the change to cmd/compile/internal/amd64/ggen.go:65 didn't fix the crash.

I think I found a second contributor or the cause; it is in runtime/stack.go:72. Both diffs are in go1-21-2-diffs.txt. Does it look reasonable?

Empirically, one or both fixes resolve the issue.

Comment From: randall77

I don't understand what the stack fix is doing. It's just increasing the reserved stack for plan9 from 512 bytes to 4096 bytes. Is there some reason plan9 needs that extra stack? It doesn't sound like it is directly related to the X15 problem.

Comment From: orangecms

Looking at ggen.go's history: https://github.com/golang/go/commit/326df693d700fa42c2740dcc89a14c821d019f52

This commit switches from using R13 to using X15, saying:

Use XORL and X15 for zeroing in ggen's zerorange on AMD64

Prefer a SSE store from X15 over XORL REG REG -> MOVQ.

Use XORL REG, REG to setup 0 for REP STOS.

Comment From: bradfitz

@cherrymui, @randall77, any thoughts on the mentioned ggen.go change?

Comment From: orangecms

See also notes here on how Plan 9 uses X15 as an exception: https://tip.golang.org/src/cmd/compile/abi-internal

Except on Plan 9, where X15 is a scratch register because SSE registers cannot be used in note handlers (so the compiler avoids using them except when absolutely necessary).

Comment From: rsc

@bradfitz pointed this bug out to me. I think we should just fix the Plan 9 kernel and then not have a weird special case to worry about in Go anymore. @9nut where can I get the sources for the kernel you are using? Thanks.

Comment From: rsc

I downloaded the latest cd image from 9p.io and worked on the kernel.

fpnotediffs.txt is the diff I've applied to allow the use of floating-point in note handlers. The note handler starts with a copy of the FP state that the process had when the note came in, but then when the note handler is done, that note-specific state is discarded, leaving the original unmodified.

fptest.c.txt is a test program that now passes.

Comment From: rsc

I've been running a 386 kernel it turns out. https://go.dev/wiki/Plan9 mentions a 9k kernel, but I followed that link and don't see any kernel configs under /sys/src/9k for amd64 kernels. All I see is k10 for objtype=k10. It also links to 9front but I downloaded their qcow2 file and it couldn't run the vga under qemu, which the 386 kernel from 9p.io had no problem with. I guess I should debug the 9front vga?

Comment From: 0intro

The 9k kernel (Jim McKie's kernel + patches) is available as part of 9legacy. It's part of the CD image available here: http://9legacy.org/download/9legacy.iso.bz2 It should run fine on either QEMU or GCE.

Comment From: rsc

I got the 9legacy kernel booted, and I tried the go1.24.0 binaries, and immediately got a crash in runtime.lockVerifyMSize. It appears the padding trick is not quite right for GOOS=plan9 and the m is a little too small.

I tried the go1.23.6 binaries, and they worked, so then I tried editing the m struct padding in go1.24.0 and running make.rc. The (unmodified) 9k10f kernel immediately panicked, and QEMU reset the screen before I could read it. I will have to arrange for serial output. Not a promising start.

Comment From: 0intro

For 9k, you could try to set the available memory to <= 3 GB.

Comment From: rsc

Interesting comment. The kernel panic is in mmuput trying to handle a page fault. I am running qemu with -m 1G. At startup the kernel prints: 1020M memory: 2832K+220M kernel, 798M user, 0M lost, which matches. So I think I'm <=3GB but the problem is definitely somehow memory related.

term% . stk
src(0xfffffffff011b786); // dumpstack+0x10
src(0xfffffffff0157b6f); // panic+0x122
src(0xfffffffff011b96c); // faultamd64+0x12d
src(0xfffffffff011aee4); // trap+0x124
src(0xfffffffff01104a3); // _intrr
//passing interrupt frame; last pc found at sp=0xfffffffff1bd35f8
src(0xfffffffff0115d4e); // mmuput+0x115
src(0xfffffffff01a4acd); // qunlock+0xbe
src(0xfffffffff019bc08); // fixfault+0x17f
src(0xfffffffff019b977); // fault+0xee
src(0xfffffffff011b91d); // faultamd64+0xde
src(0xfffffffff011aee4); // trap+0x124
src(0xfffffffff01104a3); // _intrr
//passing interrupt frame; last pc found at sp=0xfffffffff1bd3978
term% acid /n/9fat/9k10f
/n/9fat/9k10f:amd64 plan 9 boot image
/sys/lib/acid/port
/sys/lib/acid/amd64
acid: src(0xfffffffff0115d4e); // mmuput+0x115
/sys/src/9k/k10/mmu.c:214
 209                }
 210                page->daddr = x;
 211                page->next = up->mmuptp[l];
 212                up->mmuptp[l] = page;
 213                page->prev = prev;
>214                *pte = PPN(page->pa)|PteU|PteRW|PteP;
 215                if(l == 3 && x >= m->pml4->daddr)
 216                    m->pml4->daddr = x+1;
 217            }
 218            prev = page;
 219        }
acid: src(0xfffffffff01a4acd); // qunlock+0xbe
/sys/src/9k/port/qlock.c:92
 87         ready(p);
 88         return;
 89     }
 90     q->locked = 0;
 91     q->qpc = 0;
>92     unlock(&q->use);
 93 }
 94 
 95 void
 96 rlock(RWlock *q)
 97 {
acid: src(0xfffffffff019bc08); // fixfault+0x17f
/sys/src/9k/port/fault.c:197
 192        qunlock(&s->lk);
 193    
 194        if(dommuput)
 195            mmuput(addr, mmuphys, *pg);
 196    
>197        return 0;
 198    }
 199    
 200    void
 201    pio(Segment *s, uintptr addr, uintptr soff, Page **p, int color)
 202    {
acid: 

Comment From: rsc

For what it's worth, I reproduced this on the 9k amd64 kernel using Go 1.21.0. Then I changed to a kernel that allows floating point and SSE in note handlers and deleted all the isPlan9 code from amd64/ggen.go and related places (full diff below), and that made the crash go away. So that approach seems like a good path forward.

The crash also seems to be gone at current master, although I can't tell whether that's because something got fixed independently or something got stirred around.

The one wrinkle is that I have a basic test of floating point registers surviving interruption by a note during a computation, and it fails on the 9k amd64 kernel, both with and without my kernel patch, at least on qemu (both using tcg on M3 Mac and using kvm on AMD Linux). I wouldn't want to commit to using SSE until we figure out that problem too.

diff --git a/src/cmd/compile/internal/amd64/ggen.go b/src/cmd/compile/internal/amd64/ggen.go
index db98a22a1e..1dc952a455 100644
--- a/src/cmd/compile/internal/amd64/ggen.go
+++ b/src/cmd/compile/internal/amd64/ggen.go
@@ -10,12 +10,8 @@ import (
    "cmd/compile/internal/types"
    "cmd/internal/obj"
    "cmd/internal/obj/x86"
-   "internal/buildcfg"
 )

-// no floating point in note handlers on Plan 9
-var isPlan9 = buildcfg.GOOS == "plan9"
-
 // DUFFZERO consists of repeated blocks of 4 MOVUPSs + LEAQ,
 // See runtime/mkduff.go.
 const (
@@ -64,7 +60,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.

    if cnt == 8 {
        p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off)
-   } else if !isPlan9 && cnt <= int64(8*types.RegSize) {
+   } else if cnt <= int64(8*types.RegSize) {
        for i := int64(0); i < cnt/16; i++ {
            p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off+i*16)
        }
@@ -72,7 +68,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, state *uint32) *obj.
        if cnt%16 != 0 {
            p = pp.Append(p, x86.AMOVUPS, obj.TYPE_REG, x86.REG_X15, 0, obj.TYPE_MEM, x86.REG_SP, off+cnt-int64(16))
        }
-   } else if !isPlan9 && (cnt <= int64(128*types.RegSize)) {
+   } else if cnt <= int64(128*types.RegSize) {
        // Save DI to r12. With the amd64 Go register abi, DI can contain
        // an incoming parameter, whereas R12 is always scratch.
        p = pp.Append(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_DI, 0, obj.TYPE_REG, x86.REG_R12, 0)
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index 113875861c..c599188f14 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -6,7 +6,6 @@ package amd64

 import (
    "fmt"
-   "internal/buildcfg"
    "math"

    "cmd/compile/internal/base"
@@ -1058,9 +1057,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
    case ssa.OpAMD64CALLstatic, ssa.OpAMD64CALLtail:
        if s.ABI == obj.ABI0 && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABIInternal {
            // zeroing X15 when entering ABIInternal from ABI0
-           if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
-               opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
-           }
+           opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
            // set G register from TLS
            getgFromTLS(s, x86.REG_R14)
        }
@@ -1071,9 +1068,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
        s.Call(v)
        if s.ABI == obj.ABIInternal && v.Aux.(*ssa.AuxCall).Fn.ABI() == obj.ABI0 {
            // zeroing X15 when entering ABIInternal from ABI0
-           if buildcfg.GOOS != "plan9" { // do not use SSE on Plan 9
-               opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
-           }
+           opregreg(s, x86.AXORPS, x86.REG_X15, x86.REG_X15)
            // set G register from TLS
            getgFromTLS(s, x86.REG_R14)
        }
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index 43f9f0affc..a1d8edbfd8 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -11,7 +11,6 @@ import (
    "cmd/compile/internal/types"
    "cmd/internal/obj"
    "cmd/internal/src"
-   "internal/buildcfg"
 )

 // A Config holds readonly compilation information.
@@ -377,18 +376,6 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat boo
    c.ABI0 = abi.NewABIConfig(0, 0, ctxt.Arch.FixedFrameSize)
    c.ABI1 = abi.NewABIConfig(len(c.intParamRegs), len(c.floatParamRegs), ctxt.Arch.FixedFrameSize)

-   // On Plan 9, floating point operations are not allowed in note handler.
-   if buildcfg.GOOS == "plan9" {
-       // Don't use FMA on Plan 9
-       c.UseFMA = false
-
-       // Don't use Duff's device and SSE on Plan 9 AMD64.
-       if arch == "amd64" {
-           c.noDuffDevice = true
-           c.useSSE = false
-       }
-   }
-
    if ctxt.Flag_shared {
        // LoweredWB is secretly a CALL and CALLs on 386 in
        // shared mode get rewritten by obj6.go to go through
diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s
index edf0909a77..32b1f7d8a3 100644
--- a/src/runtime/asm_amd64.s
+++ b/src/runtime/asm_amd64.s
@@ -1668,9 +1668,7 @@ TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0
 TEXT ·sigpanic0(SB),NOSPLIT,$0-0
    get_tls(R14)
    MOVQ    g(R14), R14
-#ifndef GOOS_plan9
    XORPS   X15, X15
-#endif
    JMP ·sigpanic<ABIInternal>(SB)

 // gcWriteBarrier informs the GC about heap pointer writes.