diff options
| author | Martin Hořeňovský | 2023-12-13 17:07:59 +0100 |
|---|---|---|
| committer | Martin Hořeňovský | 2023-12-13 17:24:23 +0100 |
| commit | 3acb8b30f1ce0c801de0e3880ea1f6467dd0105c (patch) | |
| tree | 9ea19e3e5aaed557cd3c5a71616ced0c81b83cfb /docs | |
| parent | 3f23192e55008f9a4b00590f7c6b5e87f273b099 (diff) | |
| download | catch3-3acb8b30f1ce0c801de0e3880ea1f6467dd0105c.tar.gz | |
More detailed examples for lifetimes in combined matcher exprs
Example taken from #2777
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/matchers.md | 42 |
1 files changed, 30 insertions, 12 deletions
diff --git a/docs/matchers.md b/docs/matchers.md index b96cc5f4..d5be1f5a 100644 --- a/docs/matchers.md +++ b/docs/matchers.md @@ -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 requires `#include <catch2/matchers/catch_matchers_string.hpp>`. +### Combining operators and lifetimes + **IMPORTANT**: The combining operators do not take ownership of the -matcher objects being combined. This means that if you store 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): +matcher objects being combined. + +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 -#include <catch2/catch_test_macros.hpp> -#include <catch2/matchers/catch_matchers_string.hpp> +CHECK_THAT(value, WithinAbs(0, 2e-2) && !WithinULP(0., 1)); +``` -TEST_CASE("Bugs, bugs, bugs", "[Bug]"){ - std::string str = "Bugs as a service"; +and so is this +```cpp +auto is_close_to_zero = WithinAbs(0, 2e-2); +auto is_zero = WithinULP(0., 1); - auto match_expression = Catch::Matchers::EndsWith( "as a service" ) || - (Catch::Matchers::StartsWith( "Big data" ) && !Catch::Matchers::ContainsSubstring( "web scale" ) ); - REQUIRE_THAT(str, match_expression); -} +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 |