Skip to content

runtime: fragile write barrier implementation #80196

Description

@randall77

Add the following patch to the runtime:

--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -194,9 +194,14 @@ func wbMove(typ *_type, dst, src unsafe.Pointer) {
        // pass a type here.
        //
        // See the comment on bulkBarrierPreWrite.
+       nop()
        bulkBarrierPreWrite(uintptr(dst), uintptr(src), typ.PtrBytes, typ)
 }
 
+//go:noinline
+//go:nosplit
+func nop() {}
+

The patch should not do anything.

But this program starts panicing:

package main

type S [5]*byte

//go:noinline
func f() S {
	return S{}
}

var sink *S

//go:noinline
func g() (s S) {
	sink = &s
	s = f()
	return
}

func main() {
	for i := range 1000000 {
		s := g()
		if s != (S{}) {
			println(s[0], s[1], s[2], s[3], s[4])
			panic(i)
		}
	}
}

The reason is we introduced a spill of wbMove's arguments at the start of wbMove. That spill uses its arg area as the spill location. At the write barrier for the s = f() statement, the results of the f() call are stored in that same argument area. We end up clobbering the results before they can be copied out to the heap.

An unwritten rule is that wbMove (and probably wbZero also) can't spill to its argument area. it is used when its argument area is currently being used for other things.

There is code to handle this situation, but I (mistakenly) thought we didn't need it for archs with register calling convention. But that only avoids the problem when marshaling the args to wbMove. It does not avoid the problem of spills inside wbMove.

We should either disable the register calling convention hack, or move wbMove to assembly. I think probably the former.

Note that this is not currently a problem for any of our releases, including tip. I just ran into it when developing a CL. Mostly filing this just as a marker to add a test off of.

Metadata

Metadata

Assignees

Labels

compiler/runtimeIssues related to the Go compiler and/or runtime.

Type

No type

Fields

No fields configured for issues without a type.

Projects

Status
Done

Relationships

None yet

Development

No branches or pull requests

Issue actions