More detailed examples for lifetimes in combined matcher exprs

Example taken from #2777
This commit is contained in:
Martin Hořeňovský 2023-12-13 17:07:59 +01:00
parent 3f23192e55
commit 3acb8b30f1
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A

View File

@ -50,25 +50,43 @@ Both of the string matchers used in the examples above live in the
`catch_matchers_string.hpp` header, so to compile the code above also `catch_matchers_string.hpp` header, so to compile the code above also
requires `#include <catch2/matchers/catch_matchers_string.hpp>`. requires `#include <catch2/matchers/catch_matchers_string.hpp>`.
### Combining operators and lifetimes
**IMPORTANT**: The combining operators do not take ownership of the **IMPORTANT**: The combining operators do not take ownership of the
matcher objects being combined. This means that if you store combined matcher objects being combined.
matcher object, you have to ensure that the matchers being combined
outlive its last use. What this means is that the following code leads
to a use-after-free (UAF):
This means that if you store combined matcher object, you have to ensure
that the individual matchers being combined outlive the combined matcher.
Note that the negation matcher from `!` also counts as combining matcher
for this.
Explained on an example, this is fine
```cpp ```cpp
#include <catch2/catch_test_macros.hpp> CHECK_THAT(value, WithinAbs(0, 2e-2) && !WithinULP(0., 1));
#include <catch2/matchers/catch_matchers_string.hpp>
TEST_CASE("Bugs, bugs, bugs", "[Bug]"){
std::string str = "Bugs as a service";
auto match_expression = Catch::Matchers::EndsWith( "as a service" ) ||
(Catch::Matchers::StartsWith( "Big data" ) && !Catch::Matchers::ContainsSubstring( "web scale" ) );
REQUIRE_THAT(str, match_expression);
}
``` ```
and so is this
```cpp
auto is_close_to_zero = WithinAbs(0, 2e-2);
auto is_zero = WithinULP(0., 1);
CHECK_THAT(value, is_close_to_zero && !is_zero);
```
but this is not
```cpp
auto is_close_to_zero = WithinAbs(0, 2e-2);
auto is_zero = WithinULP(0., 1);
auto is_close_to_but_not_zero = is_close_to_zero && !is_zero;
CHECK_THAT(a_value, is_close_to_but_not_zero); // UAF
```
because `!is_zero` creates a temporary instance of Negation matcher,
which the `is_close_to_but_not_zero` refers to. After the line ends,
the temporary is destroyed and the combined `is_close_to_but_not_zero`
matcher now refers to non-existent object, so using it causes use-after-free.
## Built-in matchers ## Built-in matchers