June 26th, 2023

Using Key Events with warning C26800

Dmitry Kobets
Software Engineer

MSVC’s Code Analysis Key Events are a useful tool for quickly getting to the root-cause of your static analysis warnings. This blog post highlights how Key Events may be used with warning C26800 Use of a moved from object to quickly narrow down the problematic code.

C26800 warning recap

Warning C26800 catches code that attempts to use an object that is in a moved-from state:

#include <iostream>
int main()
{
    std::string string_owner = "this string will be moved!";
    std::string new_string_owner = std::move(string_owner);
    std::cout << string_owner << std::endl; // Warning C26800 Use of a moved from object: ''string_owner'' (lifetime.1).
}

Instead of this string will be moved!, the print statement in the above example will produce an empty output line when run.

The move from string_owner into new_string_owner happens on the line immediately preceding its use in the print statement, so it’s instantly clear why the output is incorrect. But this is a trivial example. Real world code is rarely this straightforward.

A more complex example, sans Key Events

Consider a more convoluted example. The following code defines a function that is not only significantly longer than in the first code snippet, but is also littered with all sorts of intervening business logic (represented by // ...).

#include <string>
void take_ownership(std::string&&) { /* ... */ }
void use_string(std::string) { /* ... */ }
void my_buggy_function(bool some_condition, bool some_other_condition)
{
    std::string my_string{ "this is a string!" };
    bool my_flag{ false };

    // ...

    if (some_condition)
    {
        take_ownership(std::move(my_string)); // (a)
    }

    // ...

    if (some_other_condition) // (b)
    {
        my_flag = true;

        // ...

        my_string = "the string is reset"; // (c)

        // ...
    }

    // ...

    if (my_flag && !some_other_condition) // (d)
    {
        take_ownership(std::move(my_string)); // (e)
    }

    // ...

    use_string(my_string); // Use of a moved from object: ''my_string'' (lifetime.1). (f)
}

As in the simple example, a developer tasked with solving this bug will be looking to answer the question: where is my_string moved? A natural approach would likely be to start on line (f), and to scan backwards through the code. This process would look something like:

  • Starting at the warning location and looking a few lines up, the take_ownership(std::move(my_string)); on line (e) immediately offers itself up as the culprit. To realize that this is a red-herring requires evaluating the condition on line (d), which requires tracing even higher up into the function’s depths, delving further into the confusing business logic.
  • Eventually, once satisfied that the move on line (e) is actually unreachable (oops!), the developer recommences the upwards traversal, eventually landing on the move on line (a). But not before noticing the write to my_string on line (c).
  • The developer must now solve the puzzle of why this re-assignment (on line (c)) is not preventing the warning all the way down on line (f).
  • After spending far too long running back and forth through the code, the developer finally convinces themselves that it might be possible for the condition on line (b) to be false, thus skipping the reset on line (c). The mystery is finally solved.

There must be an easier way. Enter Key Events.

Solution made simple with Key Events

As of Visual Studio 17.5 Preview 1, Key Events have been enabled for the warning C26800. Using the Microsoft SARIF Viewer extension, this check can now precisely explain its assumptions and reasoning to the curious developer, all via inline annotations in the buggy code!

Key Events with warning C26800

These Key Events offer precisely the troublesome code path that the developer was searching for and (eventually) arrived at via manual inspection of the function’s code. Notably, we see that the check:

  • is concerned with the move on line (a), helpfully noting that its guarding condition some_condition could be true
  • points out that there is nothing stopping the conditions on line (b) and (d) from being false

The buggy control flow leading to the usage of the moved-from my_string variable is therefore immediately made clear to the satisfied developer, and the investigation time is greatly reduced.

Summary

Prior to the addition of Key Events to MSVC’s warning C26800, complex code made it hard to debug and get to the root-cause of the warning. Now, with Key Events enabled by default for warning C26800, developers can much more easily identify and resolve instances of use-after-free warnings in their code. This feature was enabled in Visual Studio 17.5 Preview 1, so please download a recent version and try it out. You can share your thoughts and comments with us through Developer Community, Twitter (@VisualC), or via email at visualcpp@microsoft.com.

Author

Dmitry Kobets
Software Engineer

0 comments

Discussion are closed.