[erlang-bugs] enif_make_int64 => -0 for INT64_MIN with gcc 4.9.1

Tuncer Ayaz tuncer.ayaz@REDACTED
Mon Jul 21 00:34:25 CEST 2014


On Sun, Jul 20, 2014 at 11:56 PM, Tomas Abrahamsson wrote:
> Hi, that sanitizer branch was most useful! I hope it will make it
> into erlang. This is a run of the previous code with the sanitizers
> merged into OTP-17.1.1:

There are more sanitizers you can try out. Usan is comparatively
low-impact, but the others cause considerable slowdowns, so don't be
surprised about that. Asan and Tsan are not cheap, but easier to use
than Valgrind. Also, you can try the same sanitizers with clang, but
the names might differ.

> Erlang/OTP 17 [erts-6.1.2] [source-cc894a7] [smp:4:4]
> [async-threads:10] [kernel-poll:false]
>
> beam/utils.c:453:14: runtime error: negation of -9223372036854775808
> cannot be represented in type 'long long int'; cast to an unsigned
> type to negate this value to itself
>
> beam/big.c:1548:4: runtime error: negation of -9223372036854775808
> cannot be represented in type 'long long int'; cast to an unsigned
> type to negate this value to itself

I just tried and found beam/big.c:1548:4, too.

> The relevant files do indeed look like they are negating INT64_MIN.
> (by the way, in utils.c, the erts_bld_sint64 takes else branch,
> as expected, but advances szp by 0.)
>
> Cramming in an (unsigned long long int) cast in there
> like the sanitizer error says, actually does make it
> print -9223372036854775808 in the erlang code,
> and no sanitizer error is printed anymore.
>
>   utils.c
>    443  Eterm
>    444  erts_bld_sint64(Uint **hpp, Uint *szp, Sint64 si64)
>    445  {
>    446      Eterm res = THE_NON_VALUE;
>    447      if (IS_SSMALL(si64)) {
>    448          if (hpp)
>    449              res = make_small((Sint) si64);
>    450      }
>    451      else {
>    452          if (szp)
>    453              *szp += ERTS_SINT64_HEAP_SIZE(si64);
>    454          if (hpp)
>    455              res = erts_sint64_to_big(si64, hpp);
>    456      }
>    457      return res;
>    458  }
>
>   big.h
>    101  #define ERTS_SINT64_HEAP_SIZE(X)                                \
>    102    (IS_SSMALL((X))                                               \
>    103     ? 0                                                          \
>    104     : ERTS_UINT64_BIG_HEAP_SIZE__((X) >= 0 ? (X) : -(X)))
>
>   big.c
>   1540  Eterm erts_sint64_to_big(Sint64 x, Eterm **hpp)
>   1541  {
>   1542      Eterm *hp = *hpp;
>   1543      int neg;
>   1544      if (x >= 0)
>   1545          neg = 0;
>   1546      else {
>   1547          neg = 1;
>   1548          x = -x;
>   1549      }
>             ...
>
> I tried changing big.h:104 into:
>             : ERTS_UINT64_BIG_HEAP_SIZE__((X) >= 0 ? (X) : -((unsigned
> long long int)X)))
> and big.c:1548 into:
>                    x = -(unsigned long long int)x;
>
> This could be an interesting quick check property, to verify no
> undefined behaviours for various programs and values.
>
> BRs
> Tomas
>
> On Sun, Jul 20, 2014 at 10:00 PM, Tuncer Ayaz wrote:
> > On Sun, Jul 20, 2014 at 3:27 AM, Tomas Abrahamsson wrote:
> > > Hi,
> > >
> > > I upgraded gcc from version 4.8.1 to 4.9.1, recompiled Erlang/OTP-17.1.1
> > > and my nif, and now enif_make_int64 creates -0 for INT64_MIN.
> > >
> > >   % make test
> > >   erl -s int64nif go -s erlang halt
> > >   Erlang/OTP 17 [erts-6.1.1] [source] [smp:4:4] [async-threads:10]
> > > [kernel-poll:false]
> > >
> > >   -0
> > >
> > > When Erlang was compiled with gcc-4.8.1, it printed -9223372036854775808,
> > > I've attached the test programs, here are the important lines:
> > >
> > >     go() ->
> > >         io:format("~p~n", [int64_from_nif()]).
> > >
> > > The NIF C-code contains:
> > >
> > >     static ERL_NIF_TERM
> > >     int64_from_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
> > >     {
> > >         return enif_make_int64(env, INT64_MIN);
> > >     }
> > >
> > > System information:
> > >
> > >   OS: linux, 32-bit core i5, 3.14-1-686-pae (debian unstable)
> > >   Erlang/OTP: built from scratch (both times) from the OTP-17.1.1 git tag.
> > >   gcc: initially: gcc (Debian 4.8.1-10) 4.8.1
> > >        with this one, it prints -9223372036854775808
> > >   gcc: after upgrade: gcc (Debian 4.9.1-1) 4.9.1
> > >        with this one, it prints -0
> > >
> > > The only change I did was apt-get install gcc and then
> > > rebuilding Erlang.
> >
> > I can confirm this on linux amd64.
> >
> > 17.1.2  gcc 4.8.2 -> -9223372036854775808
> > 17.1.2  gcc 4.9.0 -> -0
> > 17.1.2  gcc 4.9.1 -> -0
> >
> > 16B03-1 gcc 4.8.2 -> -9223372036854775808
> > 16B03-1 gcc 4.9.0 -> 0
> > 16B03-1 gcc 4.9.1 -> 0
> >
> >
> > I suppose you're not able to distill this to stand-alone C code, right?
> >
> > I don't think it will reveal anything, but it's an easy way to
> > find bugs, so maybe you can try building with
> > -fsanitize=undefined. If you want to do that, I'd suggest to apply
> > the patch available at https://github.com/erlang/otp/pull/429 and
> > run "configure --enable-sanitizers=undefined".



More information about the erlang-bugs mailing list