1
0
mirror of https://github.com/golang/go synced 2024-09-28 19:24:29 -06:00

cmd/compile: do not devirtualize defer/go calls

For defer/go calls, the function/method value are evaluated immediately.
So after devirtualizing, it may trigger a panic when implicitly deref
a nil pointer receiver, causing the program behaves unexpectedly.

It's safer to not devirtualizing defer/go calls at all.

Fixes #52072

Change-Id: I562c2860e3e577b36387dc0a12ae5077bc0766bf
Reviewed-on: https://go-review.googlesource.com/c/go/+/428495
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
Cuong Manh Le 2022-09-06 12:54:54 +07:00 committed by Gopher Robot
parent 812fd2fe70
commit c82304b712
2 changed files with 50 additions and 2 deletions

View File

@ -17,9 +17,25 @@ import (
// Func devirtualizes calls within fn where possible.
func Func(fn *ir.Func) {
ir.CurFunc = fn
// For promoted methods (including value-receiver methods promoted to pointer-receivers),
// the interface method wrapper may contain expressions that can panic (e.g., ODEREF, ODOTPTR, ODOTINTER).
// Devirtualization involves inlining these expressions (and possible panics) to the call site.
// This normally isn't a problem, but for go/defer statements it can move the panic from when/where
// the call executes to the go/defer statement itself, which is a visible change in semantics (e.g., #52072).
// To prevent this, we skip devirtualizing calls within go/defer statements altogether.
goDeferCall := make(map[*ir.CallExpr]bool)
ir.VisitList(fn.Body, func(n ir.Node) {
if call, ok := n.(*ir.CallExpr); ok {
Call(call)
switch n := n.(type) {
case *ir.GoDeferStmt:
if call, ok := n.Call.(*ir.CallExpr); ok {
goDeferCall[call] = true
}
return
case *ir.CallExpr:
if !goDeferCall[n] {
Call(n)
}
}
})
}

View File

@ -0,0 +1,32 @@
// run
// Copyright 2022 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 main
type I interface{ M() }
type T struct {
x int
}
func (T) M() {}
var pt *T
func f() (r int) {
defer func() { recover() }()
var i I = pt
defer i.M()
r = 1
return
}
func main() {
if got := f(); got != 1 {
panic(got)
}
}