ericniebler / range-v3

Range library for C++14/17/20, basis for C++20's std::ranges
Other
4.14k stars 439 forks source link

Undefined behavior caught by GCC 9 on Ubuntu 20.04 when using `views::cache1` #1603

Open Marc--Olivier opened 3 years ago

Marc--Olivier commented 3 years ago

Hi there,

I recently upgraded my OS from Ubuntu 18.04 to Ubuntu 20.04 and since, my unit tests that were using views::cache1 started to fail. The code repeatedly returns the error ../deps/range-v3/include/range/v3/view/cache1.hpp:35:12: runtime error: load of value 255, which is not a valid value for type 'bool', where the value 255 varies. So it seems that the problem is triggered by the bool undefined behavior sanitizer from GCC 9 (-fsanitize=bool should be enough to reproduce the problem).

I would be very grateful if someone could have a look at this issue...

Below is a shell script that should help to reproduce the error:

export CC=gcc-9
export CXX=g++-9
RANGE_V3_BRANCH=0.10.0

tmp_dir="$(mktemp -d)"
test_project_dir="$tmp_dir/test_project"
mkdir -p "$test_project_dir"/build

## Compile range-v3 library
pushd "$tmp_dir"
git clone https://github.com/ericniebler/range-v3.git --branch "$RANGE_V3_BRANCH" --depth 1
mkdir range-v3/build
pushd range-v3/build
cmake .. -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DRANGE_V3_TESTS=OFF -DRANGE_V3_HEADER_CHECKS=OFF \
  -DRANGE_V3_DOCS=OFF -DRANGE_V3_EXAMPLES=OFF \
  -DCMAKE_INSTALL_PREFIX="$test_project_dir/deps/range-v3"
ninja && ninja install
popd

popd
pushd "$test_project_dir"/build

## Create CMake file and test C++ file.
cat <<EOF >"$test_project_dir"/CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(test_cache1)

set(CMAKE_CXX_STANDARD 17)

add_compile_options(-fno-sanitize-recover=all -fsanitize=undefined)

find_package(range-v3 REQUIRED HINTS "deps/range-v3")

add_executable(test_cache1 test_cache1.cpp)

target_link_libraries(test_cache1
  PRIVATE
    range-v3::range-v3
    -fsanitize=undefined
)
EOF

cat <<EOF >"$test_project_dir"/test_cache1.cpp
#include <range/v3/view.hpp>
#include <iostream>

using namespace ranges;

int main(int argc, char* const argv[]) {
  auto v2 = views::iota(1,argc+9)
    | views::transform([](int i) { return i+1; })
    // Similar issue when using
    // | views::remove_if([](int i) { return i % 2 == 1;})
    | views::cache1
    | views::transform([](int i) { return i+1; })
    | to<std::vector>();
  std::cout << v2.at(0) << std::endl;
}
EOF

## Compile and run...
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Debug
ninja && ./test_cache1

If you don't have a Linux Ubuntu 20.04 installed on your computer, you can use a Docker image:

# Start the Docker image with `docker run -it ubuntu:focal-20210119`.
# Then run the following instructions.
apt-get update
DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends \
  ca-certificates \
  git \
  cmake \
  ninja-build \
  g++-9

Note that the version of the compiler is:

$ g++-9 --version
g++-9 (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Many thanks in advance!


Comment edited on 28th January:

jonathanhaigh commented 3 years ago

Hello,

I'm seeing something like this too with:

root@0dbe4316802a:/work# cat cache1-ub.cpp 
#include <array>
#include <range/v3/view/cache1.hpp>
#include <range/v3/view/filter.hpp>

int main(int, const char**) {
    const auto ints = std::array{ 0, 1, 2, 3, 4 };

    auto rng = ints |
        ranges::views::cache1 |
            ranges::views::filter(
            [](int x){ return x < 2; }
        );

    auto sum = 0;
    for (const auto i : rng) {
        sum += i;
    }

    return 0;
}
root@0dbe4316802a:/work# g++ --version
g++ (Ubuntu 10.2.0-5ubuntu1~20.04) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

root@0dbe4316802a:/work# g++ -fsanitize=undefined -std=c++2a -Irange-v3-0.11.0/include cache1-ub.cpp
root@0dbe4316802a:/work# UBSAN_OPTIONS=print_stacktrace=1 ./a.out
range-v3-0.11.0/include/range/v3/view/cache1.hpp:35:12: runtime error: load of value 39, which is not a valid value for type 'bool'
    #0 0x5569cd8e7407 in ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> >::cache1_view(ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> >&&) (/work/a.out+0x9407)
    #1 0x5569cd8e7573 in auto ranges::views::all_fn::from_range_<ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> > >(ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> >&&, std::integral_constant<bool, true>, ranges::detail::ignore_t, ranges::detail::ignore_t) (/work/a.out+0x9573)
    #2 0x5569cd8e75fe in auto ranges::views::all_fn::operator()<ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> > >(ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> >&&) const (/work/a.out+0x95fe)
    #3 0x5569cd8e4557 in ranges::filter_view<decltype (ranges::views::all((declval<ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> > >)())), main::{lambda(int)#1}> ranges::views::cpp20_filter_base_fn::operator()<ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> >, main::{lambda(int)#1}>(ranges::filter_view&&, decltype (ranges::views::all((declval<ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> > >)()))) const (/work/a.out+0x6557)
    #4 0x5569cd8e4502 in decltype (((ranges::views::filter_base_fn&&){parm#1})((ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> >&&){parm#2}, (main::{lambda(int)#1}&&){parm#2})) ranges::invoke_fn::operator()<ranges::views::filter_base_fn, ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> >, main::{lambda(int)#1}>(ranges::views::filter_base_fn, ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> >, main::{lambda(int)#1}) const (/work/a.out+0x6502)
    #5 0x5569cd8e4479 in _ZNO6ranges6detail13bind_back_fn_INS_5views14filter_base_fnEJZ4mainEUliE_EEclIJNS_11cache1_viewINS_8ref_viewIKSt5arrayIiLm5EEEEEEEEEDTclL_ZNS_6invokeEEcl7declvalIS3_EEspcl7declvalIT0_EEEEDpOT_ (/work/a.out+0x6479)
    #6 0x5569cd8e42de in auto ranges::views::view_closure_base_ns::operator|<ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> >, ranges::detail::bind_back_fn_<ranges::views::filter_base_fn, main::{lambda(int)#1}> >(ranges::cache1_view<ranges::ref_view<std::array<int, 5ul> const> >&&, ranges::views::view_closure<ranges::detail::bind_back_fn_<ranges::views::filter_base_fn, main::{lambda(int)#1}> >) (/work/a.out+0x62de)
    #7 0x5569cd8e4c13 in main (/work/a.out+0x6c13)
    #8 0x7fd87d5980b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #9 0x5569cd8e412d in _start (/work/a.out+0x612d)

root@0dbe4316802a:/work#

Also on godbolt: https://godbolt.org/z/W4T1nq I can reproduce with gcc 10.1, gcc 10.2 and gcc trunk and with ranges-v3 0.11.0 and ranges-v3 trunk. Giving cache1_view::dirty_ a default value of true seems to fix the issue. I'll raise a PR.