Opened 8 years ago

Last modified 5 years ago

#10005 reopened Bugs

erf_inv_initializer crashes under valgrind

Reported by: Marcin Wojdyr <wojdyr@…> Owned by: John Maddock
Milestone: To Be Determined Component: math
Version: Boost 1.54.0 Severity: Problem
Keywords: Cc:

Description

It was reported before on the mailing list: http://lists.boost.org/boost-users/2012/08/75711.php and I assume it's because valgrind doesn't support long doubles: https://bugs.kde.org/show_bug.cgi?id=197915

Somehow erfc_inv is called in this place and the argument is then found to be 0:

         // Some compilers choke on constants that would underflow, even in code that isn't instantiated
         // so try and filter these cases out in the preprocessor:
#if LDBL_MAX_10_EXP >= 800
         if(static_cast<T>(BOOST_MATH_BIG_CONSTANT(T, 64, 1e-800)) != 0)
            boost::math::erfc_inv(static_cast<T>(BOOST_MATH_BIG_CONSTANT(T, 64, 1e-800)), Policy());

So an exception is thrown during initialization. While it may not be a bug, valgrind is extremely useful and it would be nice to have a workaround.

I don't understand why erf_inv and erfc_inv are called with these arguments in erf_inv_initializer<>::init::do_init(), so I'm not sure what workaround is safe.

Here is a full traceback:

#0  0x00000036fb435c39 in __GI_raise (sig=sig@entry=6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1  0x00000036fb437348 in __GI_abort () at abort.c:89
#2  0x00000036ff860f85 in __gnu_cxx::__verbose_terminate_handler ()
    at ../../../../libstdc++-v3/libsupc++/vterminate.cc:95
#3  0x00000036ff85eee6 in __cxxabiv1::__terminate (handler=<optimized out>)
    at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:38
#4  0x00000036ff85ef13 in std::terminate ()
    at ../../../../libstdc++-v3/libsupc++/eh_terminate.cc:48
#5  0x00000036ff85f13f in __cxxabiv1::__cxa_throw (obj=0x526e490, 
    tinfo=<optimized out>, dest=<optimized out>)
    at ../../../../libstdc++-v3/libsupc++/eh_throw.cc:84
#6  0x0000000004cb1541 in boost::throw_exception<std::overflow_error> (e=...)
    at /usr/include/boost/throw_exception.hpp:67
#7  0x0000000004cb7f28 in boost::math::policies::detail::raise_error<std::overflow_error, long double> (
    function=0x4d2d338 "boost::math::erfc_inv<%1%>(%1%, %1%)", 
    message=0x4d1bf8e "Overflow Error")
    at /usr/include/boost/math/policies/error_handling.hpp:95
#8  0x0000000004cb7f98 in boost::math::policies::detail::raise_overflow_error<long double> (function=<optimized out>, message=<optimized out>)
    at /usr/include/boost/math/policies/error_handling.hpp:211
#9  0x0000000004cdf17e in raise_overflow_error<long double, boost::math::policies::policy<boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> > (message=0x0, 
    function=<optimized out>)
    at /usr/include/boost/math/policies/error_handling.hpp:515
#10 boost::math::erfc_inv<long double, boost::math::policies::policy<boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> > (z=0, z@entry=0, pol=...)
    at /usr/include/boost/math/special_functions/detail/erf_inv.hpp:383
#11 0x0000000004cdfaae in boost::math::detail::erf_inv_initializer<long double, boost::math::policies::policy<boost::math::policies::promote_float<false>, boost::math::policies::promote_double<false>, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> >::init::do_init () at /usr/include/boost/math/special_functions/detail/erf_inv.hpp:347
#12 0x0000000004cda34b in init (this=<optimized out>)
    at /usr/include/boost/math/special_functions/detail/erf_inv.hpp:332
#13 __static_initialization_and_destruction_0 (
    __initialize_p=__initialize_p@entry=1, __priority=__priority@entry=65535)
    at /usr/include/boost/math/special_functions/detail/erf_inv.hpp:367
#14 0x0000000004cda463 in _GLOBAL__sub_I_fit.cpp(void) ()
    at ../../fityk/fit.cpp:643
#15 0x00000036fb00f2ea in call_init (l=<optimized out>, argc=argc@entry=1, 
    argv=argv@entry=0xffefffc48, env=env@entry=0xffefffc58) at dl-init.c:82
#16 0x00000036fb00f3d3 in call_init (env=<optimized out>, 
    argv=<optimized out>, argc=<optimized out>, l=<optimized out>)
    at dl-init.c:34
#17 _dl_init (main_map=0x36fb221168, argc=1, argv=0xffefffc48, env=0xffefffc58)
    at dl-init.c:130
#18 0x00000036fb00122a in _dl_start_user () from /lib64/ld-linux-x86-64.so.2

Change History (18)

comment:1 by John Maddock, 8 years ago

Oh boy this sucks :-(

You could work around the problem by defining BOOST_MATH_PROMOTE_DOUBLE_POLICY=false when building I guess.

I don't really understand why this is being triggered, the code checks that 1e-800 is non-zero before calling erc_inv(1e-800) at which point it apparently is zero after all! The argument values can't be changed BTW - they're cunningly chosen to take specific code paths and force initialization of all the static data. Or I could add some try/catch blocks I guess, I will probably experiment with that.

comment:2 by anonymous, 8 years ago

Update: I just experimented with this, and I'm unable to reproduce with current Git master and valgrind-3.8.1. Do you have a reduced test case?

comment:3 by Marcin Wojdyr <wojdyr@…>, 8 years ago

I haven't realized it before, but it depends on compiler flags. I'm using now Fedora 20, valgrind 3.9.0, GCC 4.8 and boost 1.54 and 1.55 (both give the same result). I can reproduce the problem only with g++ -Og.

But I've seen it with previous GCC versions (4.6 or 4.7, I'm not sure) and valgrind 3.8.1. I don't remember what optimization flags I used, but there was no -Og in previous versions.

The minimal program I now use for testing is:

#include <stdio.h>
#include <stdlib.h>
#include <boost/math/special_functions.hpp>
int main(int argc, char **argv) {
    double x = strtod(argv[1], NULL);
    printf("%g\n", boost::math::erfc_inv(x));
}

So as a workaround I'll just use a different compilation flags.

comment:4 by John Maddock, 8 years ago

Resolution: fixed
Status: newclosed

It does indeed seem to be specific to using -Og. After a bit of experimentation I believe I have it fixed in https://github.com/boostorg/math/commit/a29a74bdaf078b5df4f875ead18960671fd5c479

comment:5 by jon.gammell@…, 7 years ago

I am experiencing this on Ubuntu 12.04 (valgrind-3.7.0 and gcc 4.6.3) with a manually installed Boost 1.58.0.

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<std::overflow_error> >'
  what():  Error in function boost::math::erfc_inv<long double>(long double, long double): Overflow Error

It occurs when either the -O3 or -O2 flag is given, but does not occur without an -O flag. I didn't test any other optimization flag combinations.

comment:6 by John Maddock, 7 years ago

Sigh... I'll look into this again, but no promises, long double floating point values seem to behave completely unpredictably under valgrind :(

comment:7 by mlogicli <elliot.li.tech@…>, 7 years ago

I was hit by this problem using Ubuntu 15.10 LTS on x86-64, which ships with boost 1.58.0 and GCC 5.2.1. For me, this problem exists only when my program is compiled by using -O2 -g. It ran ok under valgrind when compiled without -O2. I guess GCC removes is_value_non_zero() during optimization.

comment:8 by scott_paulin@…, 7 years ago

Resolution: fixed
Status: closedreopened

I am getting this under boost version 1.54.0.1 installed from apt-get on Mint 17.2. Compiled with clang 3.4. Still occurs when no build flags are specified.

in reply to:  8 ; comment:9 by anonymous, 7 years ago

Replying to scott_paulin@…:

I am getting this under boost version 1.54.0.1 installed from apt-get on Mint 17.2. Compiled with clang 3.4. Still occurs when no build flags are specified.

Update: Works fine when I compile my code WITHOUT -std=c++11

in reply to:  9 comment:10 by anonymous, 7 years ago

Replying to anonymous:

Replying to scott_paulin@…:

I am getting this under boost version 1.54.0.1 installed from apt-get on Mint 17.2. Compiled with clang 3.4. Still occurs when no build flags are specified.

Update: Works fine when I compile my code WITHOUT -std=c++11

Sorry, the first message should have read "Still occurs when no -O build flags are specified". I can't edit that message, it would be cool if someone with admin rights could clean up my mess by editing my first message.

comment:11 by anonymous, 7 years ago

Boost-1.54 is an old Boost release now - and precedes the fixes (such as they are) above. Can you please try with Boost-1.60?

comment:12 by John Maddock, 7 years ago

I'm unable to reproduce on ubuntu with any combination of -ON and -g and either latest clang or gcc-5.1 or 4.9.2 and current develop branch (which should be the same as 1.60). Which is not to say that some folks won't experience this still.

The essential issue is that valgrind doesn't support long double fully, so finite long doubles can get truncated to zero or infinity and random points in the program (ie you can check that a variable is non-zero, and then in the next line when you go to actually use it, it's been magically truncated to zero. The result is that it completely breaks program logic).

There are a couple of possible workarounds (other than choosing -O options carefully), to define:

BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS - which prevents any long double functions from being defined or used with Boost.Math, or else define: BOOST_MATH_PROMOTE_DOUBLE_POLICY=false, which prevents double functions from being evaluated at long double precision internally, but otherwise retains the long double implementations if they're needed.

Both of these will reduce precision slightly, and both will typically improve performance on 64-bit hardware.

comment:13 by anonymous, 6 years ago

We have this problem using boost 1.60. Is there any work around?

comment:14 by anonymous, 6 years ago

I still have this problem with boost 1.64.

comment:14 by anonymous, 6 years ago

I still have this problem with boost 1.64.

comment:15 by John Maddock, 5 years ago

I'm unable to reproduce, I tried every combination of -std= and -O options I could think of with gcc-4.8.4, 6.1.0 and clang-5.

Can you provide an updated test case along with the build options and compiler version used?

comment:16 by anonymous, 5 years ago

Reproduced under Ubuntu 17.04

$ valgrind --version valgrind-3.13.0

$ gcc --version gcc (Ubuntu 7.2.0-1ubuntu2) 7.2.0
I use both -std=c++11 and -O2 flags and valgrind works fine if I remove the -O flag

comment:17 by Tom Anderson, 5 years ago

I appear to have this with valgrind 3.10.1, and Boost 1.63.0, compiled with GCC 7.2.0 on Ubuntu 14.04.5.

I'm not sure what compiler flags i'm using: Boost is a dependency of a third-party library whose build process is an impenetrable thicket.

Note: See TracTickets for help on using tickets.