3 ms·
On a quick read of the paper, I see two surprising things: 1. If there’s no initializer and various conditions are met, then “the bytes have erroneous values,
by amluto 7mo ago
On a quick read of the paper, I see two surprising things:
1. If there’s no initializer and various conditions are met, then “the bytes have erroneous values, where each value is determined by the implementation independently of the state of the program.
What does “independently” mean? Are we talking about all zeros? Is the implementation not permitted to use whatever arbitrary value was in memory? Why not?
2. What’s up with [[indeterminate]]? I would expect “indeterminate” to mean that the variable has a value that happens to be arbitrary (and may contain sensitive data, etc), not that it turns back into actual UB.
- Dylan16807 7mo ago> What does “independently” mean? It can pick whatever value it wants and doesn't have to care what the program is doing. Also the value has to stay the same until it's 'replaced'. > Are we talking about all zeros? It might be, but probably won't be. What makes you bring up all zeroes? > Is the implementation not permitted to use whatever arbitrary value was in memory? Why not? (Edit: probably wrong, also affects other things I said) It can. What suggests it wouldn't be able to? > 2. What’s up with [[indeterminate]]? I would expect “indeterminate” to mean that the variable has a value that happens to be arbitrary (and may contain sensitive data, etc), not that it turns back into actual UB. "has a value that happens to be arbitary" would be the default without [[indeterminate]]. Well, it can also error out if the compiler wants to do that.
- aw1621107 7mo ago> It can. What suggests it wouldn't be able to? "Whatever value was in memory" would be depending on the (former?) state of the program, wouldn't it?
- Dylan16807 7mo agoIf that's what they're going for, it's way too much weight to hang on a single vague word like that. Trying to define "state of the program" in a detailed way sounds nightmarish. Let's say I'm the implementation. If I go get fresh (but not zeroed) memory from the OS to put my stack on, the garbage in there isn't state of the program, right? If I then run a function and the function exits, is the garbage now state of the program, or is it outside the state of the program? If I want a fixed init value per address, is that allowed as a hardening feature or disallowed as being based on allocation patterns? Does the as-if rule apply, so I'm fine if the program can't know for sure where I got my arbitrary byte values from? And would that mean there's still no way to say "Don't waste time initializing it, but don't do any UB shenanigans either. (Basically, pretend it was initialized by a random number generator.)"
- aw1621107 7mo ago> Let's say I'm the implementation. If I go get fresh (but not zeroed) memory from the OS to put my stack on, the garbage in there isn't state of the program, right? I'd argue that once you get the memory it's now part of the state of your program, which precludes it from being involved in whatever value you end up reading from the variable(s) corresponding to that memory. > If I want a fixed init value per address, is that allowed as a hardening feature or disallowed as being based on allocation patterns? I'd guess that that specific implementation would be disallowed, but as I'm an internet nobody I'd take that with an appropriately-sized grain of salt. > And would that mean there's still no way to say "Don't waste time initializing it, but don't do any UB shenanigans either. (Basically, pretend it was initialized by a random number generator.)" I feel like you'd need something like LLVM's `freeze` intrinsic for that kind of functionality.
- aw1621107 7mo ago> What does “independently” mean? It means what it says on the tin. Whatever value ends up being used must not depend on the state of the program. > Are we talking about all zeros? All zeros is an option, but the intent is to allow the implementation to pick other values as it sees fit: > Note that we do not want to mandate that the specific value actually be zero (like P2723R1 does), since we consider it valuable to allow implementations to use different “poison” values in different build modes. Different choices are conceivable here. A fixed value is more predictable, but also prevents useful debugging hints, and poses a greater risk of being deliberately relied upon by programmers. > Is the implementation not permitted to use whatever arbitrary value was in memory? No, because the value in such a case can depend on the state of the program. > Why not? Doing so would defeat the purpose of the change, which is to turn nasal-demons-on-mistake into something with less dire consequences: > In other words, it is still an "wrong" to read an uninitialized value, but if you do read it and the implementation does not otherwise stop you, you get some specific value. In general, implementations must exhibit the defined behaviour, at least up until a diagnostic is issued (if ever). There is no risk of running into the consequences associated with undefined behaviour (e.g. executing instructions not reflected in the source code, time-travel optimisations) when executing erroneous behaviour. > What’s up with [[indeterminate]]? The idea is to provide a way to opt into the old full-UB behavior if you can't afford the cost of the new behavior. > I would expect “indeterminate” to mean that the variable has a value that happens to be arbitrary (and may contain sensitive data, etc), not that it turns back into actual UB. I believe the spelling matches how the term was used in previous standards. For example, from the C++23 standard [0] (italics in original): > When storage for an object with automatic or dynamic storage duration is obtained, the object has an indeterminate value, and if no initialization is performed for the object, that object retains an indeterminate value until that value is replaced. [0]: https://open-std.org/JTC1/SC22/WG21/docs/papers/2023/n4950.pdf https://open-std.org/JTC1/SC22/WG21/docs/papers/2023/n4950.p...
- amluto 7mo ago> Doing so would defeat the purpose of the change, which is to turn nasal-demons-on-mistake into something with less dire consequences What nasal demons? UB is permitted to format your disk, execute arbitrary code, etc. But there’s lots of room between deterministic values and UB. For example, taking a value that does depend on the previous state of the program and calling it the “erroneous” value would give a non-UB, won’t format your hard disk solution. And it even makes quite a lot of performance sense: the value that was already in the register or at that address in memory is available for free! The difference from C++23 would be that using that value would merely be erroneous and not UB. And I think the word “indeterminate” should have been reserved for that sort of behavior.
- deleted 7mo ago[deleted]