-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/cgo: missing pointer check for struct passed through a union type #15942
Comments
This looks impossible to fix. I'm open to suggestions. As soon as the Go code imports |
I think this is just a bad interaction between the fix for #14387 and the fact that we use [n]byte to represent "opaque" C unions. One fix would involve remembering details about C unions when we generate their opaque types: for example, we could check the corresponding Go types of each member of the union and record (somewhere?) whether any of the members may contain pointers. An even simpler approximation would be to merely record the set of Go types that represent C unions and make |
The current code is careful to never erroneously check a non-pointer value as though it were a pointer, as that can cause false positives and, worse, unpredictable behavior when using PIE with ASLR. We might be able to use your suggestion if we recorded where a pointer might be in the C type, and also looked at the Go code to see whether the field was in fact initialized with an actual pointer. That could in principle catch errors like your original report, though of course it would hardly be foolproof. |
In this example, we do know that the value is a pointer: it's a Changing typedef union {
header hdr;
direct d;
indirect i;
} either;
int get_payload(either *e) {
switch (e->hdr.k) {
case DIRECT:
return e->d.payload;
case INDIRECT:
return *(e->i.payload);
default:
return -1;
}
} to typedef void either;
int get_payload(either *e) {
switch (((header*)e)->k) {
case DIRECT:
return ((direct*)e)->payload;
case INDIRECT:
return *(((indirect*)e)->payload);
default:
return -1;
}
} produces a program with identical semantics that does correctly trigger the cgo pointer check, without any change at all in the user-supplied Go code. |
We know that the argument to the function is a pointer, but passing a pointer is OK. What we don't know is whether that pointer points to a pointer. In the original example, it obviously does, because |
Wouldn't the runtime use the heap mask bits to determine which fields are pointers? I still don't see how handling a pointer-to-union is fundamentally any different than handling I guess it might be different because the runtime assumes that it knows the structure of types other than unsafe.Pointer? (Could we pass another parameter to runtime.cgoCheckPointer to say "you may think you know what type this thing is but really you don't"?) |
Ah, I see what you mean. The I'll change the milestone to 1.8. |
@ianlancetaylor, is this an easy fix? |
CL https://golang.org/cl/33237 mentions this issue. |
I was thinking some more about the examples in #14210. Here's a more realistic false-negative example that does "follow the rules", but still manages to leak a Go pointer into C undetected. It's not unlike what you might find in a program using, say, the SDL API.
It doesn't do any iffy casting in C, and its only iffy casting in Go is due to language support - this is exactly the code you end up writing if you have to deal with a C API with unions.
With go version go1.6.1 linux/amd64, this program outputs
42
and exits with code 0. It should instead panic with a cgo pointer violation.The text was updated successfully, but these errors were encountered: