-
Notifications
You must be signed in to change notification settings - Fork 17.6k
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: add built-in function "formallyRead" for preventing elimination of "dead" stores #33325
Comments
My initial thought is that a builtin function The benefits to benchmarking seem less interesting to me. Using global variables is easy enough to understand, and it doesn't seem likely to fail any time soon. |
But you said in the linked mailing list discussion that:
Also, from the same discussion it seems like both you and rsc think that using sink variables complicates benchmarking code, but you say that formallyRead is not easy to understand ... Maybe the main problem is that formallyRead is not a good name? Edit: i was confused about the "unexported variable" part and thus misunderstood what GCC optimizes and what does it not optimize, sorry. |
Yes, maybe. For the benchmarking case, perhaps we could repurpose |
I think For benchmarking particularly, perhaps there should be a method on |
func wipe(key []byte) {
for i := range key {
key[i] = 0
}
runtime.KeepAlive(key)
} What's to stop the compiler from optimizing out the entire function? KeepAlive is currently implemented as: func KeepAlive(x interface{}) {
// Introduce a use of x that the compiler can't eliminate.
// This makes sure x is alive on entry. We need x to be alive
// on entry for "defer runtime.KeepAlive(x)"; see issue 21402.
if cgoAlwaysFalse {
println(x)
}
} I think the branch is there to confuse it into thinking there's a possibility of the value being needed? |
Correct. |
@randall77 A question: Suppose one passes a slice to runtime.KeepAlive (like in awnumar's example above). Does runtime.KeepAlive then protect just the slice structure (the one that has the pointer to the backing array, length and capacity; I think) or does it also protect the slice's backing array? I am asking because I think println does not need to read the slice's backing array. |
@nsajko Both. |
@randall77 Sorry for bothering you again, but, in your last message, did you mean "live" just in the GC sense, or also in the "prevents dead code elimination" sense? |
I mean in the "live" sense. Dead code elimination isn't observable in the language. |
@randall77 So when you said,
How does this hold? |
Sorry, I was talking about the language semantics. Yes, Anything passed to |
Based on the discussion above, |
Dead stores are writes to variables that are not followed by corresponding reads. While dead store elimination is mostly a desirable optimization, some of the time it is essential for program correctness that dead store elimination (or the unreachable code elimination that it sometimes implies) does not happen.
The two examples on my mind are:
Secure zeroization of sensitive data (also known as cleansing, wiping, etc.), where the store (to computer memory) itself is essential, and it should not legitimately be followed by a read.
Benchmarking, where the problem is the elimination of code that is meant to be benchmarked
In C these problems might be solved by using memset_s, volatile types or inline assembly tricks.
In current Go user code, convoluted efforts to prevent dead store elimination for wiping memory and to prevent dead code elimination for benchmarking ultimately rely only on the lack of optimizations in current Go implementations, instead of on formal correctness, which this proposal aims to enable in addition to simplification of user code.
The new built-in "formallyRead" is meant to return nothing and either
takes a pointer or an arbitrary number of pointers
takes a slice or an arbitrary number of slices
if package "unsafe" is imported it additionally can take an unsafe.Pointer and a uintptr (to indicate base and size of a memory object)
The pointer arguments to formallyRead indicate objects that must be considered as having formally been read at the point of the formallyRead invocation. Slice arguments indicate the same for the slice's underlying array in the range up to len(slice).
The following minimal example is meant to show the effects of formallyRead :
The following example showcases the cutting down of benchmark code that formallyRead would enable, because use of sink package level variables is no longer necessary:
turns into:
See also:
Regarding benchmarking:
go/src/math/all_test.go
Line 3109 in 80f9d32
https://groups.google.com/forum/#!topic/golang-dev/eCPwwvqs2Cg
A more ambitious and ambiguous proposal regarding the zeroization use case:
#21374
Discusses some ugly hacks that are done in the name of zeroization:
#21865
The text was updated successfully, but these errors were encountered: