aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunyang Shao <shaojunyang@google.com>2026-03-12 21:36:33 +0000
committerGopher Robot <gobot@golang.org>2026-04-08 05:21:35 -0700
commit3985ca0b6264835c049e601900a8072b5d1b13b4 (patch)
tree609746738e98eca53a00fcdb7cdbda52e88d7d41
parent4978c2029c3ff66da8f1dfe32ac6770ce1514fe8 (diff)
downloadgo-3985ca0b6264835c049e601900a8072b5d1b13b4.tar.xz
cmd/compile: fix mem access overlap detection
When a no-op interface conversion is wrapped around the rhs of an assignment, the memory overlap detection logic in the compiler failed to peel down conversion to see the actual pointer, causing an incorrect no-overlapping determination. Thanks to Jakub Ciolek for reporting this issue. Fixes #78371 Fixes CVE-2026-27144 Change-Id: I55ff0806b099e1447bdbfba7fde6c6597db5d65c Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/3780 Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Neal Patel <nealpatel@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/763764 Auto-Submit: David Chase <drchase@google.com> TryBot-Bypass: David Chase <drchase@google.com> Reviewed-by: Russ Cox <rsc@golang.org> Reviewed-by: Jakub Ciolek <jakub@ciolek.dev>
-rw-r--r--src/cmd/compile/internal/ssagen/ssa.go20
-rw-r--r--src/cmd/compile/internal/test/memoverlap_test.go41
2 files changed, 55 insertions, 6 deletions
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
index 952bd1b71c..6fbb533089 100644
--- a/src/cmd/compile/internal/ssagen/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -1654,6 +1654,16 @@ func (s *state) stmtList(l ir.Nodes) {
}
}
+func peelConvNop(n ir.Node) ir.Node {
+ if n == nil {
+ return n
+ }
+ for n.Op() == ir.OCONVNOP {
+ n = n.(*ir.ConvExpr).X
+ }
+ return n
+}
+
// stmt converts the statement n to SSA and adds it to s.
func (s *state) stmt(n ir.Node) {
s.pushLine(n.Pos())
@@ -1829,12 +1839,10 @@ func (s *state) stmt(n ir.Node) {
// arrays referenced are strictly smaller parts of the same base array.
// If one side of the assignment is a full array, then partial overlap
// can't happen. (The arrays are either disjoint or identical.)
- mayOverlap := n.X.Op() == ir.ODEREF && (n.Y != nil && n.Y.Op() == ir.ODEREF)
- if n.Y != nil && n.Y.Op() == ir.ODEREF {
- p := n.Y.(*ir.StarExpr).X
- for p.Op() == ir.OCONVNOP {
- p = p.(*ir.ConvExpr).X
- }
+ ny := peelConvNop(n.Y)
+ mayOverlap := n.X.Op() == ir.ODEREF && (n.Y != nil && ny.Op() == ir.ODEREF)
+ if ny != nil && ny.Op() == ir.ODEREF {
+ p := peelConvNop(ny.(*ir.StarExpr).X)
if p.Op() == ir.OSPTR && p.(*ir.UnaryExpr).X.Type().IsString() {
// Pointer fields of strings point to unmodifiable memory.
// That memory can't overlap with the memory being written.
diff --git a/src/cmd/compile/internal/test/memoverlap_test.go b/src/cmd/compile/internal/test/memoverlap_test.go
new file mode 100644
index 0000000000..c53288e6bb
--- /dev/null
+++ b/src/cmd/compile/internal/test/memoverlap_test.go
@@ -0,0 +1,41 @@
+// Copyright 2026 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package test
+
+import "testing"
+
+const arrFooSize = 96
+
+type arrFoo [arrFooSize]int
+
+//go:noinline
+func badCopy(dst, src []int) {
+ p := (*[arrFooSize]int)(dst[:arrFooSize])
+ q := (*[arrFooSize]int)(src[:arrFooSize])
+ *p = arrFoo(*q)
+}
+
+//go:noinline
+func goodCopy(dst, src []int) {
+ p := (*[arrFooSize]int)(dst[:arrFooSize])
+ q := (*[arrFooSize]int)(src[:arrFooSize])
+ *p = *q
+}
+
+func TestOverlapedMoveWithNoopIConv(t *testing.T) {
+ h1 := make([]int, arrFooSize+1)
+ h2 := make([]int, arrFooSize+1)
+ for i := range arrFooSize + 1 {
+ h1[i] = i
+ h2[i] = i
+ }
+ badCopy(h1[1:], h1[:arrFooSize])
+ goodCopy(h2[1:], h2[:arrFooSize])
+ for i := range arrFooSize + 1 {
+ if h1[i] != h2[i] {
+ t.Errorf("h1 and h2 differ at index %d, expect them to be the same", i)
+ }
+ }
+}