Skip to content

Commit b716cf1

Browse files
aykevldeadprogram
authored andcommitted
loader/libclang: fix CGo-related crash
Sometimes when a GC happens while processing a C fragment with libclang, a pointer-typed integer with value 0x1 ends up on the Go stack and the GC will trip over it. This commit changes the offending struct type to be uintptr_t instead of void*. See https://go-review.googlesource.com/c/go/+/66332 for a similar change.
1 parent 586023b commit b716cf1

File tree

2 files changed

+98
-23
lines changed

2 files changed

+98
-23
lines changed

loader/libclang.go

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,38 @@ import (
1616
/*
1717
#include <clang-c/Index.h> // if this fails, install libclang-8-dev
1818
#include <stdlib.h>
19+
#include <stdint.h>
1920
20-
int tinygo_clang_globals_visitor(CXCursor c, CXCursor parent, CXClientData client_data);
21-
int tinygo_clang_struct_visitor(CXCursor c, CXCursor parent, CXClientData client_data);
21+
// This struct should be ABI-compatible on all platforms (uintptr_t has the same
22+
// alignment etc. as void*) but does not include void* pointers that are not
23+
// always real pointers.
24+
// The Go garbage collector assumes that all non-nil pointer-typed integers are
25+
// actually pointers. This is not always true, as data[1] often contains 0x1,
26+
// which is clearly not a valid pointer. Usually the GC won't catch this issue,
27+
// but occasionally it will leading to a crash with a vague error message.
28+
typedef struct {
29+
enum CXCursorKind kind;
30+
int xdata;
31+
uintptr_t data[3];
32+
} GoCXCursor;
33+
34+
// Forwarding functions. They are implemented in libclang_stubs.c and forward to
35+
// the real functions without doing anything else, thus they are entirely
36+
// compatible with the versions without tinygo_ prefix. The only difference is
37+
// the CXCursor type, which has been replaced with GoCXCursor.
38+
GoCXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu);
39+
unsigned tinygo_clang_visitChildren(GoCXCursor parent, CXCursorVisitor visitor, CXClientData client_data);
40+
CXString tinygo_clang_getCursorSpelling(GoCXCursor c);
41+
enum CXCursorKind tinygo_clang_getCursorKind(GoCXCursor c);
42+
CXType tinygo_clang_getCursorType(GoCXCursor c);
43+
GoCXCursor tinygo_clang_getTypeDeclaration(CXType t);
44+
CXType tinygo_clang_getTypedefDeclUnderlyingType(GoCXCursor c);
45+
CXType tinygo_clang_getCursorResultType(GoCXCursor c);
46+
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
47+
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
48+
49+
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
50+
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
2251
*/
2352
import "C"
2453

@@ -123,29 +152,29 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
123152

124153
ref := refMap.Put(info)
125154
defer refMap.Remove(ref)
126-
cursor := C.clang_getTranslationUnitCursor(unit)
127-
C.clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
155+
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
156+
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
128157

129158
return nil
130159
}
131160

132161
//export tinygo_clang_globals_visitor
133-
func tinygo_clang_globals_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.int {
162+
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
134163
info := refMap.Get(unsafe.Pointer(client_data)).(*fileInfo)
135-
kind := C.clang_getCursorKind(c)
164+
kind := C.tinygo_clang_getCursorKind(c)
136165
switch kind {
137166
case C.CXCursor_FunctionDecl:
138-
name := getString(C.clang_getCursorSpelling(c))
139-
cursorType := C.clang_getCursorType(c)
167+
name := getString(C.tinygo_clang_getCursorSpelling(c))
168+
cursorType := C.tinygo_clang_getCursorType(c)
140169
if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
141170
return C.CXChildVisit_Continue // not supported
142171
}
143-
numArgs := int(C.clang_Cursor_getNumArguments(c))
172+
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
144173
fn := &functionInfo{}
145174
info.functions[name] = fn
146175
for i := 0; i < numArgs; i++ {
147-
arg := C.clang_Cursor_getArgument(c, C.uint(i))
148-
argName := getString(C.clang_getCursorSpelling(arg))
176+
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
177+
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
149178
argType := C.clang_getArgType(cursorType, C.uint(i))
150179
if argName == "" {
151180
argName = "$" + strconv.Itoa(i)
@@ -155,7 +184,7 @@ func tinygo_clang_globals_visitor(c, parent C.CXCursor, client_data C.CXClientDa
155184
typeExpr: info.makeASTType(argType),
156185
})
157186
}
158-
resultType := C.clang_getCursorResultType(c)
187+
resultType := C.tinygo_clang_getCursorResultType(c)
159188
if resultType.kind != C.CXType_Void {
160189
fn.results = &ast.FieldList{
161190
List: []*ast.Field{
@@ -166,9 +195,9 @@ func tinygo_clang_globals_visitor(c, parent C.CXCursor, client_data C.CXClientDa
166195
}
167196
}
168197
case C.CXCursor_TypedefDecl:
169-
typedefType := C.clang_getCursorType(c)
198+
typedefType := C.tinygo_clang_getCursorType(c)
170199
name := getString(C.clang_getTypedefName(typedefType))
171-
underlyingType := C.clang_getTypedefDeclUnderlyingType(c)
200+
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
172201
expr := info.makeASTType(underlyingType)
173202
if strings.HasPrefix(name, "_Cgo_") {
174203
expr := expr.(*ast.Ident)
@@ -203,8 +232,8 @@ func tinygo_clang_globals_visitor(c, parent C.CXCursor, client_data C.CXClientDa
203232
typeExpr: expr,
204233
}
205234
case C.CXCursor_VarDecl:
206-
name := getString(C.clang_getCursorSpelling(c))
207-
cursorType := C.clang_getCursorType(c)
235+
name := getString(C.tinygo_clang_getCursorSpelling(c))
236+
cursorType := C.tinygo_clang_getCursorType(c)
208237
info.globals[name] = &globalInfo{
209238
typeExpr: info.makeASTType(cursorType),
210239
}
@@ -305,7 +334,7 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
305334
underlying := C.clang_Type_getNamedType(typ)
306335
return info.makeASTType(underlying)
307336
case C.CXType_Record:
308-
cursor := C.clang_getTypeDeclaration(typ)
337+
cursor := C.tinygo_clang_getTypeDeclaration(typ)
309338
fieldList := &ast.FieldList{
310339
Opening: info.importCPos,
311340
Closing: info.importCPos,
@@ -315,8 +344,8 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
315344
info *fileInfo
316345
}{fieldList, info})
317346
defer refMap.Remove(ref)
318-
C.clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(uintptr(ref)))
319-
switch C.clang_getCursorKind(cursor) {
347+
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
348+
switch C.tinygo_clang_getCursorKind(cursor) {
320349
case C.CXCursor_StructDecl:
321350
return &ast.StructType{
322351
Struct: info.importCPos,
@@ -368,18 +397,18 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
368397
}
369398

370399
//export tinygo_clang_struct_visitor
371-
func tinygo_clang_struct_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.int {
400+
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
372401
passed := refMap.Get(unsafe.Pointer(client_data)).(struct {
373402
fieldList *ast.FieldList
374403
info *fileInfo
375404
})
376405
fieldList := passed.fieldList
377406
info := passed.info
378-
if C.clang_getCursorKind(c) != C.CXCursor_FieldDecl {
407+
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_FieldDecl {
379408
panic("expected field inside cursor")
380409
}
381-
name := getString(C.clang_getCursorSpelling(c))
382-
typ := C.clang_getCursorType(c)
410+
name := getString(C.tinygo_clang_getCursorSpelling(c))
411+
typ := C.tinygo_clang_getCursorType(c)
383412
field := &ast.Field{
384413
Type: info.makeASTType(typ),
385414
}

loader/libclang_stubs.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
// This file implements some small trampoline functions. The signatures
3+
// are slightly different from the ones defined in libclang.go, but they
4+
// should be ABI compatible.
5+
6+
#include <clang-c/Index.h> // if this fails, install libclang-8-dev
7+
8+
CXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu) {
9+
return clang_getTranslationUnitCursor(tu);
10+
}
11+
12+
unsigned tinygo_clang_visitChildren(CXCursor parent, CXCursorVisitor visitor, CXClientData client_data) {
13+
return clang_visitChildren(parent, visitor, client_data);
14+
}
15+
16+
CXString tinygo_clang_getCursorSpelling(CXCursor c) {
17+
return clang_getCursorSpelling(c);
18+
}
19+
20+
enum CXCursorKind tinygo_clang_getCursorKind(CXCursor c) {
21+
return clang_getCursorKind(c);
22+
}
23+
24+
CXType tinygo_clang_getCursorType(CXCursor c) {
25+
return clang_getCursorType(c);
26+
}
27+
28+
CXCursor tinygo_clang_getTypeDeclaration(CXType t) {
29+
return clang_getTypeDeclaration(t);
30+
}
31+
32+
CXType tinygo_clang_getTypedefDeclUnderlyingType(CXCursor c) {
33+
return clang_getTypedefDeclUnderlyingType(c);
34+
}
35+
36+
CXType tinygo_clang_getCursorResultType(CXCursor c) {
37+
return clang_getCursorResultType(c);
38+
}
39+
40+
int tinygo_clang_Cursor_getNumArguments(CXCursor c) {
41+
return clang_Cursor_getNumArguments(c);
42+
}
43+
44+
CXCursor tinygo_clang_Cursor_getArgument(CXCursor c, unsigned i) {
45+
return clang_Cursor_getArgument(c, i);
46+
}

0 commit comments

Comments
 (0)