Opened 8 years ago

Last modified 5 years ago

#10493 assigned Bugs

Since 1.56, any_range with non-reference references can cause UB

Reported by: nofitserov@… Owned by: Neil Groves
Milestone: Boost 1.64.0 Component: range
Version: Boost 1.62.0 Severity: Regression
Keywords: Cc: p.omta@…

Description

This must be related to #10360. This is a regression since 1.55.

When using any_range<T, category, T, ptrdiff_t>, mutable dereference() method returns mutable_reference_type_generator<T>::type, which becomes T&. So dereference() returns a dangling reference to an on-the-fly computed value.

See attached test case, which works with 1.55, and fails with -fsanitize=address on Clang 3.5 with 1.56.

#include <boost/range/any_range.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <cmath>
#include <iostream>

typedef boost::any_range<
    std::string,
    boost::forward_traversal_tag,
    std::string,
    std::ptrdiff_t
> range_t;

std::string f(std::string a)
{
    return a + "!";
}

int main()
{
    std::vector<std::string> v = {"a", "b"};
    range_t r = v | boost::adaptors::transformed(f);
    for (auto&& a : r)
        std::cout << a << std::endl;
    return 0;
}

Change History (8)

comment:1 by Paul Omta <p.omta@…>, 8 years ago

Cc: p.omta@… added

comment:2 by Neil Groves, 8 years ago

Milestone: To Be DeterminedBoost 1.58.0
Status: newassigned

comment:3 by anonymous, 7 years ago

Just reproduced it with boost 1.58 after migration from 1.55. I tried to play with it and found how to make it work.

Using const std::string instead of std::string, seems fix the problem

typedef boost::any_range<
    std::string,
    boost::forward_traversal_tag,
    const std::string,
    std::ptrdiff_t
> range_t;

I haven't digged into any_iterator sources but seems it behave differently when Reference is const

comment:4 by kozlov.taras@…, 7 years ago

Forget to add email in previous comment

comment:5 by Tobias Reh <treh@…>, 7 years ago

Still present in 1.59.0

The problem is becoming more visible with MSVC++2015 because the compiler is better in detecting dangling references and emitting errors for them. To hard code those checks, just add the following assert into every dereference() method in range/detail/any_iterator_wrapper.hpp:

static_assert(
   !std::is_lvalue_reference<reference>::value 
   || std::is_lvalue_reference<decltype(*m_it)>::value,
   "dangling ref"
);

A little bit of digging in the headers shows that the problem lies in range/detail/any_iterator_interface.hpp, where any_incrementable_iterator_interface's

typedef Reference reference;

was changed in 1.56.0 to

typedef typename mutable_reference_type_generator<
    Reference
>::type reference;

Rolling this back solves the problem for our codebase. Any possibilities of having this fixed soon?

comment:6 by wslcdg@…, 6 years ago

Milestone: Boost 1.58.0Boost 1.64.0
Version: Boost 1.56.0Boost 1.62.0

Problem still exists boost 1.62

comment:7 by o.andriyanov@…, 5 years ago

Still have this bug in 1.64. Adding const to Reference works around the problem but disables move construction. Is there any other workaround?

comment:8 by jeremy.coulon.jrmc@…, 5 years ago

Problem still there in boost 1.65.1

Adding const to Reference doesn't workaround the problem (at least on Visual Studio): c:\path\to\boost\1.65.1\include\boost\range\detail\any_iterator_wrapper.hpp(302) : warning C4172: returning address of local variable or temporary

Note: See TracTickets for help on using tickets.