[erlang-bugs] enif_make_badarg crashes is_binary

Sverker Eriksson sverker@REDACTED
Tue Feb 22 11:27:29 CET 2011


Mikael Pettersson wrote:
> Steve Vinoski writes:
>  > I was recently writing a nif and had one C function returning an
>  > ERL_NIF_TERM that was then being passed to a second C function. The
>  > second one checked the passed term using
>  > enif_inspect_iolist_as_binary(), and it crashed. I debugged it and
>  > it's apparently caused by the fact that the ERL_NIF_TERM being passed
>  > was returned from enif_make_badarg(), so it was a 0. It caused this
>  > error on my Macbook Pro:
>  > 
>  > Program received signal EXC_BAD_ACCESS, Could not access memory.
>  > Reason: KERN_INVALID_ADDRESS at address: 0xfffffffe
>  > 
>  > The reason for that invalid address is the is_binary(x) macro at the
>  > beginning of enif_inspect_iolist_as_binary(). It expands to
>  > 
>  >     if (((!(((((term)))) & (0x3 -0x2))) && (((*((Eterm*) (((((term)))
>  > - 0x2)))) & (0x3F -(0x3 << 2))) == (0x0|(0x8 << 2))))) {
>  > 
>  > The first test, which I believe comes from _unchecked_is_boxed(x),
>  > passes because it's testing !0 which of course is 1, then the second
>  > test which I believe is _unchecked_boxed_val(x) fails with the invalid
>  > address error because the code more or less dereferences a null
>  > pointer.
>  > 
>  > I think the fix is to make macros like is_binary(x) first check the
>  > term using is_value(x).
>
> No.  Non-values are emphatically NOT permitted inside Erlang process
> heaps, live BEAM VM registers, live terms, or as parameters to internal
> C functions (except for very few special cases).  The non-values are
> very private to the VM, and are (as I recall, it's been a while since
> I wrote that code), limited to (a) indicating exception returns from
> C BIFs (handled immediately by beam_emu), (b) temporary magic markers
> during GC, and (c) signalling error returns from some internal C
> functions.  In no case is a non-value allowed to leak into
> application-visible Erlang state.
>
> The fix, therefore, is to catch whatever produced that non-value
> (error from a C function?) and turn that into an error return or
> Erlang exception rather than trying to pass it on as a valid term.
>
> /Mikael
>
>   
Yes, Mikael is right.
The documentation could be more clear about the only valid use of 
enif_make_badarg(); return its value from the nif. You are not even 
allowed to change your mind, once enif_make_badarg() has been called, 
its return value *must* be returned from the nif.
enif_make_badarg() was a bit of a quick fix just to get the most common 
use case to work. In future we might want functions to throw customized 
exceptions. A small step in that direction could be an enif_is_value() 
(or maybe enif_is_exception) in order to  distinguish values from 
exceptions.


/Sverker, Erlang/OTP



More information about the erlang-bugs mailing list