3 ms·
It's a good decision to add at least some checks into C++ standard library. But no runtime check can find a bug in code like this: std::vector<int> v; v.pu
by Panzerschrek 11mo ago
It's a good decision to add at least some checks into C++ standard library. But no runtime check can find a bug in code like this:
std::vector<int> v;
v.push_back(123);
auto& n= v.front();
v.push_back(456);
auto n_doubled= n * 2;
A better language is needed in order to prevent such bugs, where such compile-time correctness checks are possible. Some static analyzers are able to detect it in C++, but only in some cases.
- delta_p_delta_x 11mo agoIt seems to me statically checking this should be possible. The liveness of the result of std::vector::front() should be invalidated and be considered dead after the second invocation to push_back(). Then a static analyser would correctly mark the final line with red squiggles. Of course, compilers would still be happy to compile this, which they really ought not to.
- aw1621107 11mo ago> It seems to me statically checking this should be possible. Statically checking this specific example (or similarly simple examples) could be possible, sure. I'm not so sure about more complex cases, such as opaque functions (whether because the function is literally opaque or because not enough inlining occurred), stored references (e.g., std::span), unintentional mutation of the underlying data structure, etc. Thats basically one of the main reason Rust's lifetimes exist - to explicitly encode information about when lifetimes are valid in the type system. C++ doesn't have an equivalent (yet?), so unless you're willing to use global analysis an/or non-standard annotations there's only so much static analysis can do.
- optimalsolver 11mo agoWhat does this do?
- aw1621107 11mo agoPotential use-after-free. push_back() may reallocate, which would invalidate the reference returned by front(), rendering its subsequent use invalid.
- optimalsolver 11mo agoThank you!
- tonyarkles 11mo agoIn my own experience with “modern-ish C++” (the platform I work with only supports up to C++17 for now), once we started using smart pointers, like unique_ptr and shared_ptr, iterator invalidation has been the primary source of memory safety errors. You have to be so careful any time you have a reference into a container. In a lot of cases the solution is already sitting there for you in <algorithms> though. One of the more common places we’ve encountered this problem is when someone has a simple task like “delete items from this vector that match some predicate” and then just write a for-loop that does that but doesn’t handle the fact that the iterators can go bad when you modify the vector. The algorithms library has functions in it to handle that, but without a good mental checklist of what’s all in there people will generally just do the simple (and unfortunately wrong) thing.
- optimalsolver 11mo agoHow much performance do you give up with smart pointers?
- tonyarkles 11mo agoNot enough that we’ve ever noticed it being at all significant in the profiling we do regularly. The system is an Edge ML processor running about 600 megapixel/sec through a pipeline that does pre- and post-processing on multiple CPUs and does inference on the GPU using TensorRT. In general allocation isn’t anywhere near our bottleneck, it’s all of the image processing work that is.
- GoblinSlayer 11mo agoNirvana fallacy. Some checks are better than no checks.
- lang4d 11mo agoI'd be surprised if some combination of ASAN and UBSAN wouldn't catch this and similar dangling references
- steveklabnik 11mo agoI thought it should too, but it doesn't seem to, unless I made a mistake, which I probably did: https://godbolt.org/z/Ex63vxj4r https://godbolt.org/z/Ex63vxj4r
- aw1621107 11mo agoI think you need to actually use the dangling reference: https://godbolt.org/z/f4s3fT3nM https://godbolt.org/z/f4s3fT3nM
- steveklabnik 11mo agoAh, that would make sense, yes. Thanks!