The cartesian_apply() function in TestQuiesceDb.cc had a stack-use-
after-scope issue detected by ASan. The problem was that the lambda
function at line 513 had an implicit return type deduction that
returned a value instead of a reference:
auto f = [&q](const auto &args) {
q = div(q.quot, args.size());
return args.at(q.rem); // Returns V instead of V const&
};
Even though args.at(q.rem) returns a const reference to an element
in the array, the lambda's auto return type deduction caused it to
return by value. This meant the tuple at line 518:
auto apply_tuple = std::tuple<V const &...> { f(array_args)... };
was storing references to temporary values that went out of scope
immediately after tuple construction. When std::apply() later tried
to use these references, it accessed freed stack memory.
Fix by explicitly specifying the lambda's return type as decltype(auto)
to preserve the reference semantics:
auto f = [&q](const auto &args) -> decltype(auto) {
q = div(q.quot, args.size());
return args.at(q.rem); // Now returns V const& as intended
};
This ensures the lambda returns a reference to the array element,
which remains valid for the lifetime of array_args (the entire scope
of cartesian_apply()). This preserves the original optimization of
avoiding copies while fixing the use-after-scope issue.
Signed-off-by: Kefu Chai <k.chai@proxmox.com>
// we use parameter pack expansion as part of the brace initializer
// to perform sequential calculation of the
- auto f = [&q](const auto &args) {
+ // Lambda returns reference to avoid copying and ensure lifetime validity
+ auto f = [&q](const auto &args) -> decltype(auto) {
q = div(q.quot, args.size());
return args.at(q.rem);
};
- auto apply_tuple = std::tuple<V const &...> {
+ auto apply_tuple = std::tuple<V const &...> {
f(array_args)
- ...
+ ...
};
if (!std::apply(func, apply_tuple)) {