Catching and debugging invalid use of a local variable reference inside a moved lambda

I came across a difficult to debug situation in one of my real projects, where I accidentally got a link to a local variable inside a moved lambda. Access was made from another thread, but the moved lambda was maintained until the second thread ended.

The error occurred only when optimization was disabled and was caused by careless refactoring.

I created a minimal example ( available here on wandbox ) that reproduces the problem:

struct state { int x = 100; }; template <typename TF> void eat1(TF&& f) { // Call the lambda. f(); // Simulate waiting for the second thread // to finish. std::this_thread::sleep_for(1000ms); } template <typename TF> void eat0(TF&& f) { // Move the lambda to some other handler. eat1(std::forward<TF>(f)); } void use_state(state& s) { // Will print `100`. std::cout << sx << "\n"; // Separate thread. Note that `s` is captured by // reference. std::thread t{[&s] { // Simulate computation delay. std::this_thread::sleep_for(500ms); // Will print garbage. std::cout << sx << "\n"; }}; t.detach(); } int main() { eat0([] { // Local lambda variable that will be accessed // after the lambda is moved. state s; // Function that takes `s` by reference and // accesses it in a separate thread after the // lambda is moved. use_state(s); }); } 

Surprisingly, not one of the disinfectants and warning flags could help here.

I have tried the following combinations of compilers and disinfectants:

 -Wall -Wextra -Wpedantic -g -O0 

flags are always enabled:

  • Compiled by: g ++ 6.1.1 on Arch Linux x64; clang ++ 3.8.0 on Arch Linux x64; g ++ 5.3.1 on Fedora x64; clang ++ 3.7.0 on Fedora x64.

  • Disinfectants: -fsanitize=address ; -fsanitize=undefined , -fsanitize=thread .

None of the combinations produced any useful diagnosis. I expected either AddressSanitizer to tell me that I was accessing a sagging link, or UndefinedSanitizer to catch UB when accessing it, or ThreadSanitizer to tell me that a separate thread has access to an invalid memory location.

Is there a reliable way to diagnose this problem? Should I put this example in any of the disinfectant error tracing as a function request / defect?

+6
source share
1 answer

The valgrind memcheck tool caught this problem at default settings. Nevertheless, such unpleasant mistakes have a chance to avoid memories. I am not sure that the problem will be solved in a real program.

The fact that the first lambda was moved is not relevant to the problem (although perhaps this complicates the debugging process). The problem is related to accessing the local variable in the function that completed its execution (again, the fact that the access came from another thread only complicated the investigation, but did not affect the error). The fact that the first lambda remained alive should by no means protect you - local variables relate to calling the lambda, not the lambda itself.

+4
source

All Articles