]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/cgo: run cgo pointer checks for pointer to union
authorIan Lance Taylor <iant@golang.org>
Mon, 14 Nov 2016 23:51:30 +0000 (15:51 -0800)
committerIan Lance Taylor <iant@golang.org>
Tue, 15 Nov 2016 17:10:52 +0000 (17:10 +0000)
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>
misc/cgo/errors/ptr.go
src/cmd/cgo/gcc.go

index e39f0413e4799eaf8e1e91386cb5d3998deb54c6..4dafbdf3c011766b99a53e1e118dfce2e3f76cab 100644 (file)
@@ -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() {
index 812e315eaf6520621e12ff21f08cdfbc0b6f4739..d88c41d50a161898ab50594882e222be7334e83a 100644 (file)
@@ -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
@@ -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
@@ -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)
@@ -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)
                        }
@@ -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]
@@ -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 ""