Skip to content

Commit 46f60d6

Browse files
committed
cmd/cgo: reject attempts to declare methods on C types
This change causes cgo to emit an error (with the same message as the compiler) when it encounters a declaration of a method whose receiver type is C.T or *C.T. Conceptually, C is another package, but unfortunately the desugaring of C.T is a type within the same package, causing the previous behavior to accept invalid input. It is likely that at least some users are intentionally exploiting this behavior, so this may break their build. We should mention it in the release notes. Fixes #57926 Change-Id: I513cffb7e13bc93d08a07b7e61301ac1762fd42d Reviewed-on: https://go-review.googlesource.com/c/go/+/490819 Reviewed-by: Ian Lance Taylor <[email protected]> Run-TryBot: Bryan Mills <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Bryan Mills <[email protected]>
1 parent a9a01ea commit 46f60d6

File tree

2 files changed

+79
-20
lines changed

2 files changed

+79
-20
lines changed

src/cmd/cgo/ast.go

+48-20
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package main
99
import (
1010
"fmt"
1111
"go/ast"
12+
"go/format"
1213
"go/parser"
1314
"go/scanner"
1415
"go/token"
@@ -62,29 +63,48 @@ func (f *File) ParseGo(abspath string, src []byte) {
6263
// In ast1, find the import "C" line and get any extra C preamble.
6364
sawC := false
6465
for _, decl := range ast1.Decls {
65-
d, ok := decl.(*ast.GenDecl)
66-
if !ok {
67-
continue
68-
}
69-
for _, spec := range d.Specs {
70-
s, ok := spec.(*ast.ImportSpec)
71-
if !ok || s.Path.Value != `"C"` {
72-
continue
73-
}
74-
sawC = true
75-
if s.Name != nil {
76-
error_(s.Path.Pos(), `cannot rename import "C"`)
77-
}
78-
cg := s.Doc
79-
if cg == nil && len(d.Specs) == 1 {
80-
cg = d.Doc
66+
switch decl := decl.(type) {
67+
case *ast.GenDecl:
68+
for _, spec := range decl.Specs {
69+
s, ok := spec.(*ast.ImportSpec)
70+
if !ok || s.Path.Value != `"C"` {
71+
continue
72+
}
73+
sawC = true
74+
if s.Name != nil {
75+
error_(s.Path.Pos(), `cannot rename import "C"`)
76+
}
77+
cg := s.Doc
78+
if cg == nil && len(decl.Specs) == 1 {
79+
cg = decl.Doc
80+
}
81+
if cg != nil {
82+
f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), abspath)
83+
f.Preamble += commentText(cg) + "\n"
84+
f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n"
85+
}
8186
}
82-
if cg != nil {
83-
f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), abspath)
84-
f.Preamble += commentText(cg) + "\n"
85-
f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n"
87+
88+
case *ast.FuncDecl:
89+
// Also, reject attempts to declare methods on C.T or *C.T.
90+
// (The generated code would otherwise accept this
91+
// invalid input; see issue #57926.)
92+
if decl.Recv != nil && len(decl.Recv.List) > 0 {
93+
recvType := decl.Recv.List[0].Type
94+
if recvType != nil {
95+
t := recvType
96+
if star, ok := unparen(t).(*ast.StarExpr); ok {
97+
t = star.X
98+
}
99+
if sel, ok := unparen(t).(*ast.SelectorExpr); ok {
100+
var buf strings.Builder
101+
format.Node(&buf, fset, recvType)
102+
error_(sel.Pos(), `cannot define new methods on non-local type %s`, &buf)
103+
}
104+
}
86105
}
87106
}
107+
88108
}
89109
if !sawC {
90110
error_(ast1.Package, `cannot find import "C"`)
@@ -542,3 +562,11 @@ func (f *File) walk(x interface{}, context astContext, visit func(*File, interfa
542562
}
543563
}
544564
}
565+
566+
// If x is of the form (T), unparen returns unparen(T), otherwise it returns x.
567+
func unparen(x ast.Expr) ast.Expr {
568+
if p, isParen := x.(*ast.ParenExpr); isParen {
569+
x = unparen(p.X)
570+
}
571+
return x
572+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[short] skip
2+
[!cgo] skip
3+
4+
# Test that cgo rejects attempts to declare methods
5+
# on the types C.T or *C.T; see issue #57926.
6+
7+
! go build
8+
stderr 'cannot define new methods on non-local type C.T'
9+
stderr 'cannot define new methods on non-local type \*C.T'
10+
! stderr 'Alias'
11+
12+
-- go.mod --
13+
module example.com
14+
go 1.12
15+
16+
-- a.go --
17+
package a
18+
19+
/*
20+
typedef int T;
21+
*/
22+
import "C"
23+
24+
func (C.T) f() {}
25+
func (recv *C.T) g() {}
26+
27+
// The check is more education than enforcement,
28+
// and is easily defeated using a type alias.
29+
type Alias = C.T
30+
func (Alias) h() {}
31+
func (*Alias) i() {}

0 commit comments

Comments
 (0)