-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
proposal: cmd/cgo: add #cgo nocheckpointer annotation #62425
Comments
The whole point of cgocheck and runtime.Pinner to to ensure your C code interacts properly with the Go garbage collector. Disabling or circumventing this check doesn't address the problem, it just means you are introducing undefined behavior that could cause the application to crash or misbehave. Definitely I don't think you should be recommending people use Looking through the source code, it appears Envoy incorrectly relies on the exact layout of slices and strings even though the Go documentation states this is not guaranteed. Making matters worse, the return value of (By the way, your original code has a bug. You initialize |
@rittneje Thanks for your reply.
Actually, cgo will generate headers for C users:
yep, we rely on the exact layout, but we don't think this layout will change frequently.
Yep, we know what For Golang, it just provides another way to disable cgocheck at the C function-level scope, just like it provides For us, Golang users, we hope to use the low-level capability for better performance(avoid memory copying), just like
Yep, we have to do memory copying without disabling
Oh, thanks, it is a bug, it makes the slice grow again, we'll fix it soon. |
No, you didn't. The garbage collector has no idea you passed unpinned pointer to C. Hence the error from cgocheck. The garbage collector could very well free that memory while your C code is using it. Once that occurs, all bets are off. |
That depends.
But these C functions only use the Go pointers before they return, since we wrote these C functions and we know the details of the C code. For |
The garbage collector is also allowed to move values from one memory location to another, which would have equally bad results. The short of it is, your code violates the rules of cgo and thus is broken. And adding your proposed API seems to serve no purpose other than to allow library authors to hide their broken implementations from consumers. (It would be akin to asking the runtime not to do bounds checking on arrays.) |
To agree with @rittneje in the clearest way possible: If cgocheck=1 or cgocheck=2 complain, then your code is incorrect and violates the cgo pointer passing rules. cgocheck=0 doesn't exist to avoid warnings, but to increase performance of already correct code. Setting cgocheck=0 shouldn't be done lightly, and most certainly shouldn't be done at the code's behest. |
Yes to other languages' GC, i.e. Java. The only case I know that golang may move values is goroutine stack grow or shrink(and only for the stack values). The key pointer is:
Please see this example:
So, passing Go pointers to C is safe. Then, what does And this is why we said: "we followed the Go garbage collector rules".
@dominikh Nope, I do not think so. please see the above details. |
@doujiang24 Again, you are NOT following the rules of cgo. You clearly decided to ignore them and instead go by the current implementation, which is irrelevant. Instead of this proposal, which amounts to "let my library silently ignore the rules of cgo", you should instead focus on your actual problem - the inability to efficiently pass a list of strings from Go to C. |
Yep, let's check the doc, there is the rule of cgo:
There is also an option to disable this check:
Why need the rule of cgo?
So, the key pointer is: follow the rules of Go GC. This is the fundamental. Rules of cgo should be reasonable, and they can be changed when there is a more reasonable way.
Not following the current rules of cgo, the code must be a bug then? If you are saying this is the rule, we are done, okay, I got it.
Yep, totally agree. |
This is the heart of the problem. The rules of cgo are the garbage collector rules. That it happens to work today is not relevant. This is no different than writing code that relies on some internal implementation detail of the compiler instead of the actual language spec.
That is not what they are protecting. It has always been legal to pass a top-level pointer from Go to C, provided that C does not retain the pointer after the function call. cgocheck does not and cannot attempt to protect against the C code breaking that rule. In other words, cgocheck has nothing to do with trusting the C code, that's on the developer. What they are protecting is misuse in which you are passing pointers from Go to C in a way that is not visible to the garbage collector. Namely, you cannot pass un-pinned non-top-level pointers from Go to C. Even if it happens to work today, such misuse is subject to breakages in future Go releases.
It is as I stated above. You essentially need to be able to pass the return value of |
Nope, I don't think so. As we said above: "Rules of cgo should be reasonable, and they can be changed when there is a more reasonable way."
Could you please explain it? Why does GC need it visible?
Golang already exposes the pointer to users, and even allows passing pointers to C, so, I don't think Go GC can change to move values in the feature releases, without breaking the backward compatibility.
Thank you. let me explain it a bit. Actually, we could create a CL to shut down this check, it should work then. Okay, we can say that we have investigated the cgo for a long time, and we have made some improvements for cgo in the past year. I think the base rule that we discuss should be: what is good for Golang, and which could make Golang better. To Golang team:
|
IIUC the rule that a nested pointer cannot be passed into C is because as a managed language, pointer load/store may require barriers, which means that they can only be performed by the Go compiled code, and passing nested indirection to C may violate this requirement.
We can simply pin the referents of the pointers passed to cgo.
This is simply not true, of course the Go code has to trust the C code to call it and expect a sensible result. The check is to protect against developer not conforming to the rules of cgo and violating the gc's assumption regarding the memory state of the program. Also, I see it really backward to annotate the trustfulness of "bad C code" from C, at least it should be declared on the Go side instead. |
Just because you don't like the rules does not mean you get to ignore them without consequence. To be honest, I'm tired of having to post the exact same responses. I won't be responding anymore. |
I do think reading the Go pointers in C code is safe, before it's freed by GC.
Sorry, the "bad code" that I mentioned above is only the C code that does not follow the Go GC rules, i.e. change the values of Go pointers.
Yep, I do think it's declared on the Go side, it's declared in the comments before |
The background of the cgo pointer passing rules is https://go.googlesource.com/proposal/+/refs/heads/master/design/12416-cgo-pointers.md and #12416. We are not going to add a new annotation for the purpose of making it easier to break the rules. The rules are there for a reason. Closing as infeasible. |
design
After adding the
nocheckpointer
annotation for a C function, cgo compiler will skip generatingcgocheckpointer
for all of the arguments of the C function.That means the checkpointer always will be disabled, even
cgocheck=1
in theGODEBUG
env.why disable checkpointer
Background: we had built the Envoy Golang extension, that supports people using pure Golang to implement plugins to extend Envoy without recompile the Envoy binary, i.e. authorization plugins, waf plugins and more.
In the Envoy Golang extension, we have such a case:
We are passing a header map through a
[]string
to C, which will causecheckpointer
panic.But, if we don't passing
[]string
, we do need another memory allocation & copy for those strings, in Go side, before passing to C.Now, we require people set
GODEBUG=cgocheck=0
to disbalecgocheck
while starting Envoy.why not using runtime.Pinner
with
runtime.Pinner
, the code will be longer, 7 lines added:and worse, when a string (key or value) is not a Go pointer, i.e. a constant string,
Content-Type
,Pin
will panic:panic: runtime error: runtime.Pinner.Pin: argument is not a Go pointer
why not use GODEBUG=cgocheck=0
Yep, it does work fine.
But it requires people to set it, while people may forget it, i.e. envoyproxy/envoy#25178
And it takes effect globally, as an SDK provider, we hope people do not need to care about it, and people can still enable
cgocheck
in other their-self codes.summary
runtime.Pinner
noescape
andnocallback
annotations.Any suggestions/feedback are welcome, thank you very much!
The text was updated successfully, but these errors were encountered: