Skip to content

Commit d91ec5b

Browse files
cmd/cgo, runtime: recognize unsafe.Pointer(&s[0]) in cgo pointer checks
It's fairly common to call cgo functions with conversions to unsafe.Pointer or other C types. Apply the simpler checking of address expressions when possible when the address expression occurs within a type conversion. Change-Id: I5187d4eb4d27a6542621c396cad9ee4b8647d1cd Reviewed-on: https://go-review.googlesource.com/18391 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent e84dad3 commit d91ec5b

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

misc/cgo/errors/ptr.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ var ptrTests = []ptrTest{
106106
body: `i := 0; p := &S{p:&i, s:[]unsafe.Pointer{nil}}; C.f(&p.s[0])`,
107107
fail: false,
108108
},
109+
{
110+
// Passing the address of a slice of an array that is
111+
// an element in a struct, with a type conversion.
112+
name: "slice-ok-3",
113+
c: `void f(void* p) {}`,
114+
imports: []string{"unsafe"},
115+
support: `type S struct { p *int; a [4]byte }`,
116+
body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`,
117+
fail: false,
118+
},
109119
{
110120
// Passing the address of a static variable with no
111121
// pointers doesn't matter.

src/cmd/cgo/gcc.go

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -626,9 +626,7 @@ func (p *Package) rewriteCall(f *File, call *ast.CallExpr, name *Name) {
626626

627627
// Add optional additional arguments for an address
628628
// expression.
629-
if u, ok := call.Args[i].(*ast.UnaryExpr); ok && u.Op == token.AND {
630-
c.Args = p.checkAddrArgs(f, c.Args, u.X)
631-
}
629+
c.Args = p.checkAddrArgs(f, c.Args, call.Args[i])
632630

633631
// _cgoCheckPointer returns interface{}.
634632
// We need to type assert that to the type we want.
@@ -773,7 +771,19 @@ func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool {
773771
// only pass the slice or array if we can refer to it without side
774772
// effects.
775773
func (p *Package) checkAddrArgs(f *File, args []ast.Expr, x ast.Expr) []ast.Expr {
776-
index, ok := x.(*ast.IndexExpr)
774+
// Strip type conversions.
775+
for {
776+
c, ok := x.(*ast.CallExpr)
777+
if !ok || len(c.Args) != 1 || !p.isType(c.Fun) {
778+
break
779+
}
780+
x = c.Args[0]
781+
}
782+
u, ok := x.(*ast.UnaryExpr)
783+
if !ok || u.Op != token.AND {
784+
return args
785+
}
786+
index, ok := u.X.(*ast.IndexExpr)
777787
if !ok {
778788
// This is the address of something that is not an
779789
// index expression. We only need to examine the
@@ -804,6 +814,42 @@ func (p *Package) hasSideEffects(f *File, x ast.Expr) bool {
804814
return found
805815
}
806816

817+
// isType returns whether the expression is definitely a type.
818+
// This is conservative--it returns false for an unknown identifier.
819+
func (p *Package) isType(t ast.Expr) bool {
820+
switch t := t.(type) {
821+
case *ast.SelectorExpr:
822+
if t.Sel.Name != "Pointer" {
823+
return false
824+
}
825+
id, ok := t.X.(*ast.Ident)
826+
if !ok {
827+
return false
828+
}
829+
return id.Name == "unsafe"
830+
case *ast.Ident:
831+
// TODO: This ignores shadowing.
832+
switch t.Name {
833+
case "unsafe.Pointer", "bool", "byte",
834+
"complex64", "complex128",
835+
"error",
836+
"float32", "float64",
837+
"int", "int8", "int16", "int32", "int64",
838+
"rune", "string",
839+
"uint", "uint8", "uint16", "uint32", "uint64", "uintptr":
840+
841+
return true
842+
}
843+
case *ast.StarExpr:
844+
return p.isType(t.X)
845+
case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType,
846+
*ast.MapType, *ast.ChanType:
847+
848+
return true
849+
}
850+
return false
851+
}
852+
807853
// unsafeCheckPointerName is given the Go version of a C type. If the
808854
// type uses unsafe.Pointer, we arrange to build a version of
809855
// _cgoCheckPointer that returns that type. This avoids using a type
@@ -832,6 +878,8 @@ func (p *Package) unsafeCheckPointerName(t ast.Expr) string {
832878
func (p *Package) hasUnsafePointer(t ast.Expr) bool {
833879
switch t := t.(type) {
834880
case *ast.Ident:
881+
// We don't see a SelectorExpr for unsafe.Pointer;
882+
// this is created by code in this file.
835883
return t.Name == "unsafe.Pointer"
836884
case *ast.ArrayType:
837885
return p.hasUnsafePointer(t.Elt)

src/runtime/cgocall.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ func cgoCheckPointer(ptr interface{}, args ...interface{}) interface{} {
354354
t := ep._type
355355

356356
top := true
357-
if len(args) > 0 && t.kind&kindMask == kindPtr {
357+
if len(args) > 0 && (t.kind&kindMask == kindPtr || t.kind&kindMask == kindUnsafePointer) {
358358
p := ep.data
359359
if t.kind&kindDirectIface == 0 {
360360
p = *(*unsafe.Pointer)(p)
@@ -365,6 +365,10 @@ func cgoCheckPointer(ptr interface{}, args ...interface{}) interface{} {
365365
aep := (*eface)(unsafe.Pointer(&args[0]))
366366
switch aep._type.kind & kindMask {
367367
case kindBool:
368+
if t.kind&kindMask == kindUnsafePointer {
369+
// We don't know the type of the element.
370+
break
371+
}
368372
pt := (*ptrtype)(unsafe.Pointer(t))
369373
cgoCheckArg(pt.elem, p, true, false, cgoCheckPointerFail)
370374
return ptr

0 commit comments

Comments
 (0)