Opened 3 years ago

Last modified 17 months ago

#11633 assigned Support Requests

chrono IO V1 may throw bad_cast

Reported by: Sébastien Barthélémy <barthelemy@…> Owned by: viboes
Milestone: To Be Determined Component: chrono
Version: Boost 1.58.0 Severity: Problem
Keywords: Cc:

Description

I had occurences of chrono io throwing bad_cast exceptions, which lead to backtraces like this one:

    #0  0x00007ffff54fda30 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
    #1  0x00007ffff554f2a2 in std::__throw_bad_cast() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
    #2  0x00007ffff2289bab in std::use_facet<boost::chrono::duration_punct<char> > (__loc=...) at /usr/include/c++/4.8/bits/locale_classes.tcc:137
    #3  0x00007ffff2288c61 in boost::chrono::operator<< <char, std::char_traits<char>, boost::rational<int>, boost::ratio<1l, 1l> > (os=..., d=...) at /home/sbarthelemy/.local/share/qi/toolchains/linux64/boost/include/boost/chrono/io_v1/chrono_io.hpp:210

I think the attached minimal example reproduces the issue.

Here the compile & run log using boost 1.58

$ make clean && make test
rm -f src/*.o src/chrono_io
clang++ -std=c++11 -c -g -fPIC -Iinclude -I/usr/include -o src/chrono_io.o src/chrono_io.cc
clang++ -std=c++11 -rdynamic -Lsrc -o src/chrono_io src/chrono_io.o -lboost_chrono -lboost_system -lpthread -L/usr/lib/x86_64-linux-gnu
LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu ./src/chrono_io
terminate called after throwing an instance of 'std::bad_cast'
  what():  std::bad_cast
Aborted (core dumped)
Makefile:13: recipe for target 'test' failed
make: *** [test] Error 13

Here is the code around boost/include/boost/chrono/io_v1/chrono_io.hpp:210

template <class CharT, class Traits, class Rep, class Period>                  
std::basic_ostream<CharT, Traits>&                                             
operator<<(std::basic_ostream<CharT, Traits>& os, const duration<Rep, Period>& d)
{                                                                              
    typedef duration_punct<CharT> Facet;                                       
    std::locale loc = os.getloc();                                             
    if (!std::has_facet<Facet>(loc))                                           
        os.imbue(std::locale(loc, new Facet));                                 
    const Facet& f = std::use_facet<Facet>(os.getloc()); //<<<<<<< line210, throw here
    return os << d.count() << ' ' << f.template name<Period>(d.count());       
}

maybe we could avoid calling os.getloc() again at line 210 to avoid the race?

Attachments (1)

chrono_io_bad_cast.tgz (701 bytes) - added by Sébastien Barthélémy <barthelemy@…> 3 years ago.

Download all attachments as: .zip

Change History (12)

Changed 3 years ago by Sébastien Barthélémy <barthelemy@…>

Attachment: chrono_io_bad_cast.tgz added

comment:1 Changed 3 years ago by viboes

Hi,

I didn't through to multi-threading while developing this code :(

Could you try to comment the line? Could you also try version v2 by defining before including any boost/chrono/... file

#define BOOST_CHRONO_VERSION 2
#include <boost/chrono/chrono.hpp>
#include <boost/chrono/chrono_io.hpp>

comment:2 Changed 3 years ago by viboes

The following avoids the race, but breaks other functionalities

template <class CharT, class Traits, class Rep, class Period>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, const duration<Rep, Period>& d)
{
    typedef duration_punct<CharT> Facet;
    Facet f;
    return os << d.count() << ' ' << f.template name<Period>(d.count());
}

I tried with version 2 and I was not able to reproduce the issue. Please let me know.

comment:3 Changed 3 years ago by viboes

For what I understand std::ios_base::imbue is not thread-safe.

Version 1 is bugged as it does an imbue if the facet is not present, raising possible undefined behavior.

Version 2 duration operator<< doesn't imbues the facet by itself. If not present, it uses duration_put.

comment:4 Changed 3 years ago by viboes

Component: Nonechrono
Owner: set to viboes
Status: newassigned

comment:5 Changed 3 years ago by Sébastien Barthélémy <barthelemy@…>

Hi Vincente, thank you for answering so quickly.

I tried with version 2 and I was not able to reproduce the issue. Please let me know.

same here.

For what I understand std::ios_base::imbue is not thread-safe. Version 1 is bugged as it does an imbue if the facet is not present, raising possible undefined behavior. Version 2 duration operator<< doesn't imbues the facet by itself. If not present, it uses duration_put.

would it be possible to fix v1? Maybe with something like this (untested):

template <class CharT, class Traits, class Rep, class Period>                  
std::basic_ostream<CharT, Traits>&                                             
operator<<(std::basic_ostream<CharT, Traits>& os, const duration<Rep, Period>& d)
{                                       
    typedef duration_punct<CharT> Facet;
    os << d.count() << ' ';
    try {
        os << std::use_facet<Facet>(os.getloc()).template name<Period>(d.count());
    } catch (std::bad_cast) {
       // os locale does not have the facet
       os << Facet().template name<Period>(d.count());
    }
    return os;
}

comment:6 Changed 3 years ago by viboes

Have you tried this? This is in some way what version 2 does.

Last edited 3 years ago by viboes (previous) (diff)

comment:7 in reply to:  6 Changed 3 years ago by Sébastien Barthélémy <barthelemy@…>

Replying to viboes:

Have you tried this? This is in some way what version 2 does.

No (as the "untested" suggested), and sadly I'm by no way an iostream/locale expert.

comment:8 Changed 3 years ago by viboes

Please, could you test it?

comment:9 Changed 3 years ago by viboes

PING!!!

comment:10 Changed 3 years ago by viboes

Type: BugsSupport Requests

Moved to support until someone respond.

comment:11 Changed 17 months ago by viboes

Summary: chrono IO may throw bad_castchrono IO V1 may throw bad_cast
Note: See TracTickets for help on using tickets.