Skip to content

Commit

Permalink
cmd/cgo: run cgo pointer checks for pointer to union
Browse files Browse the repository at this point in the history
If a C union type (or a C++ class type) can contain a pointer field,
then run the cgo checks on pointers to that type. This will test the
pointer as though it were an unsafe.Pointer, and will crash if it points
to Go memory that contains a pointer.

Fixes #15942.

Change-Id: Ic2d07ed9648d4b27078ae7683e26196bcbc59fc9
Reviewed-on: https://go-review.googlesource.com/33237
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information
ianlancetaylor committed Nov 15, 2016
1 parent fab3fca commit 27b6847
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 0 deletions.
21 changes: 21 additions & 0 deletions misc/cgo/errors/ptr.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,27 @@ var ptrTests = []ptrTest{
body: `p := &C.s{}; defer C.f(p); p.p = new(C.int)`,
fail: true,
},
{
// Check a pointer to a union if the union has any
// pointer fields.
name: "union1",
c: `typedef union { char **p; unsigned long i; } u; void f(u *pu) {}`,
imports: []string{"unsafe"},
body: `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`,
fail: true,
},
{
// Don't check a pointer to a union if the union does
// not have any pointer fields.
// Like ptrdata1 above, the uintptr represents an
// integer that happens to have the same
// representation as a pointer.
name: "union2",
c: `typedef union { unsigned long i; } u; void f(u *pu) {}`,
imports: []string{"unsafe"},
body: `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`,
fail: false,
},
}

func main() {
Expand Down
56 changes: 56 additions & 0 deletions src/cmd/cgo/gcc.go
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@ func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool {
if !top {
return true
}
// Check whether this is a pointer to a C union (or class)
// type that contains a pointer.
if unionWithPointer[t.X] {
return true
}
return p.hasPointer(f, t.X, false)
case *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
return true
Expand Down Expand Up @@ -1418,6 +1423,10 @@ var tagGen int
var typedef = make(map[string]*Type)
var goIdent = make(map[string]*ast.Ident)

// unionWithPointer is true for a Go type that represents a C union (or class)
// that may contain a pointer. This is used for cgo pointer checking
var unionWithPointer = make(map[ast.Expr]bool)

func (c *typeConv) Init(ptrSize, intSize int64) {
c.ptrSize = ptrSize
c.intSize = intSize
Expand Down Expand Up @@ -1706,6 +1715,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
t.Size = t1.Size
t.Align = t1.Align
t.Go = t1.Go
if unionWithPointer[t1.Go] {
unionWithPointer[t.Go] = true
}
t.EnumValues = nil
t.Typedef = ""
t.C.Set("%s "+dt.Qual, t1.C)
Expand Down Expand Up @@ -1740,6 +1752,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
switch dt.Kind {
case "class", "union":
t.Go = c.Opaque(t.Size)
if c.dwarfHasPointer(dt, pos) {
unionWithPointer[t.Go] = true
}
if t.C.Empty() {
t.C.Set("__typeof__(unsigned char[%d])", t.Size)
}
Expand Down Expand Up @@ -1782,6 +1797,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
goIdent[name.Name] = name
sub := c.Type(dt.Type, pos)
t.Go = name
if unionWithPointer[sub.Go] {
unionWithPointer[t.Go] = true
}
t.Size = sub.Size
t.Align = sub.Align
oldType := typedef[name.Name]
Expand Down Expand Up @@ -2163,6 +2181,44 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
return
}

// dwarfHasPointer returns whether the DWARF type dt contains a pointer.
func (c *typeConv) dwarfHasPointer(dt dwarf.Type, pos token.Pos) bool {
switch dt := dt.(type) {
default:
fatalf("%s: unexpected type: %s", lineno(pos), dt)
return false

case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.EnumType,
*dwarf.FloatType, *dwarf.ComplexType, *dwarf.FuncType,
*dwarf.IntType, *dwarf.UcharType, *dwarf.UintType, *dwarf.VoidType:

return false

case *dwarf.ArrayType:
return c.dwarfHasPointer(dt.Type, pos)

case *dwarf.PtrType:
return true

case *dwarf.QualType:
return c.dwarfHasPointer(dt.Type, pos)

case *dwarf.StructType:
for _, f := range dt.Field {
if c.dwarfHasPointer(f.Type, pos) {
return true
}
}
return false

case *dwarf.TypedefType:
if dt.Name == "_GoString_" || dt.Name == "_GoBytes_" {
return true
}
return c.dwarfHasPointer(dt.Type, pos)
}
}

func upper(s string) string {
if s == "" {
return ""
Expand Down

0 comments on commit 27b6847

Please sign in to comment.