NIF Problem

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

NIF Problem

Travis Jensen
I'm getting a bus violation after my NIF code is run. The code is pretty straightforward: I'm extending crypto:aes_ctr_encrypt/3 to allow for real streaming encryption. Unfortunately, something is unhappy because after I return from the BIF, I get the bus violation.  I figure I'm either missing something stupid-obvious or there are some side-effects I'm not aware of in some of the enif_* functions.

I've included my function (aes_ctr_encrypt_with_state as well as the original aes_ctr_encrypt.  State is an arity-4 tuple containing the key (an iolist or binary), the IV (a binary of length 16), ECount (a binary of length 16 initialized to all zeros), and Num (an integer set to 0). There is another function that (correctly) initializes that (it is Erlang code and working fine). (Code also available at http://pastebin.com/fPs7SgiR)

Any ideas or thoughts would be much appreciated.
  1. static ERL_NIF_TERM aes_ctr_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  2. {/* ({Key, IVec, ECount, Num}, Data) */    
  3.     ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin;
  4.     AES_KEY aes_key;
  5.     unsigned int num = 0;
  6.     ERL_NIF_TERM ret, num2_term, cipher_term, ivec2_term, ecount2_term, new_state_term;
  7.     int state_arity;
  8.     const ERL_NIF_TERM *state_term;
  9.     unsigned char * ivec2_buf;
  10.     unsigned char * ecount2_buf;
  11.  
  12.     if (!enif_get_tuple(env, argv[0], &state_arity, &state_term)
  13.         || state_arity != 4
  14.         || !enif_inspect_iolist_as_binary(env, state_term[0], &key_bin)
  15.         || AES_set_encrypt_key(key_bin.data, key_bin.size*8, &aes_key) != 0
  16.         || !enif_inspect_binary(env, state_term[1], &ivec_bin) || ivec_bin.size != 16
  17.         || !enif_inspect_binary(env, state_term[2], &ecount_bin) || ecount_bin.size != AES_BLOCK_SIZE
  18.         || !enif_get_uint(env, state_term[3], &num)
  19.         || !enif_inspect_iolist_as_binary(env, argv[1], &text_bin)) {
  20.         return enif_make_badarg(env);
  21.     }
  22.  
  23.     ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term);
  24.     ecount2_buf = enif_make_new_binary(env, ecount_bin.size, &ecount2_term);
  25.    
  26.     memcpy(ivec2_buf, ivec_bin.data, 16);
  27.     memcpy(ecount2_buf, ecount_bin.data, ecount_bin.size);
  28.  
  29.     AES_ctr128_encrypt((unsigned char *) text_bin.data,
  30.                        enif_make_new_binary(env, text_bin.size, &cipher_term),
  31.                        text_bin.size, &aes_key, ivec2_buf, ecount2_buf, &num);
  32.  
  33.     num2_term = enif_make_uint(env, num);
  34.     new_state_term = enif_make_tuple4(env, key_bin, ivec2_term, ecount2_term, num2_term);
  35.     ret = enif_make_tuple2(env, new_state_term, cipher_term);
  36.     return ret;
  37. }
  38.  
  39.  
  40. static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  41. {/* (Key, IVec, Data) */    
  42.     ErlNifBinary key, ivec, text;
  43.     AES_KEY aes_key;
  44.     unsigned char ivec_clone[16]; /* writable copy */
  45.     unsigned char ecount_buf[AES_BLOCK_SIZE];
  46.     unsigned int num = 0;
  47.     ERL_NIF_TERM ret;
  48.  
  49.     if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
  50.         || AES_set_encrypt_key(key.data, key.size*8, &aes_key) != 0
  51.         || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16
  52.         || !enif_inspect_iolist_as_binary(env, argv[2], &text)) {
  53.         return enif_make_badarg(env);
  54.     }
  55.     memcpy(ivec_clone, ivec.data, 16);    
  56.     memset(ecount_buf, 0, sizeof(ecount_buf));
  57.     AES_ctr128_encrypt((unsigned char *) text.data,
  58.                        enif_make_new_binary(env, text.size, &ret),
  59.                        text.size, &aes_key, ivec_clone, ecount_buf, &num);
  60.  
  61.     /* To do an incremental {en|de}cryption, the state to to keep between calls
  62.         must include ivec_clone, ecount_buf and num. */
  63.     return ret;
  64. }
--
Travis Jensen

Read the Software Maven @ http://softwaremaven.innerbrane.com/
Read my LinkedIn profile @ http://www.linkedin.com/in/travisjensen
Read my Twitter mumblings @ http://twitter.com/SoftwareMaven
Send me email @ [hidden email]

*What kind of guy calls himself the Software Maven???*


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: NIF Problem

Jachym Holecek
# Travis Jensen 2011-04-28:

> [... This results in SIGBUS after returning to Erlang ...]
>
>  1. static ERL_NIF_TERM aes_ctr_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM
>     argv[])
>  2. {/* ({Key, IVec, ECount, Num}, Data) */    
>  3.     ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin;
>  4.     AES_KEY aes_key;
>  5.     unsigned int num = 0;
>  6.     ERL_NIF_TERM ret, num2_term, cipher_term, ivec2_term, ecount2_term, new_state_term;
>  7.     int state_arity;
>  8.     const ERL_NIF_TERM *state_term;
>  9.     unsigned char * ivec2_buf;
> 10.     unsigned char * ecount2_buf;
> 11.  
> 12.     if (!enif_get_tuple(env, argv[0], &state_arity, &state_term)
> 13.         || state_arity != 4
> 14.         || !enif_inspect_iolist_as_binary(env, state_term[0], &key_bin)
> 15.         || AES_set_encrypt_key(key_bin.data, key_bin.size*8, &aes_key) != 0
> 16.         || !enif_inspect_binary(env, state_term[1], &ivec_bin) || ivec_bin.size != 16
> 17.         || !enif_inspect_binary(env, state_term[2], &ecount_bin) || ecount_bin.size !=
>     AES_BLOCK_SIZE
> 18.         || !enif_get_uint(env, state_term[3], &num)
> 19.         || !enif_inspect_iolist_as_binary(env, argv[1], &text_bin)) {
> 20.         return enif_make_badarg(env);
> 21.     }
> 22.  
> 23.     ivec2_buf = enif_make_new_binary(env, ivec_bin.size, &ivec2_term);
> 24.     ecount2_buf = enif_make_new_binary(env, ecount_bin.size, &ecount2_term);
> 25.    
> 26.     memcpy(ivec2_buf, ivec_bin.data, 16);
> 27.     memcpy(ecount2_buf, ecount_bin.data, ecount_bin.size);
> 28.  
> 29.     AES_ctr128_encrypt((unsigned char *) text_bin.data,
> 30.                        enif_make_new_binary(env, text_bin.size, &cipher_term),
> 31.                        text_bin.size, &aes_key, ivec2_buf, ecount2_buf, &num);
> 32.  
> 33.     num2_term = enif_make_uint(env, num);
> 34.     new_state_term = enif_make_tuple4(env, key_bin, ivec2_term, ecount2_term, num2_term);
> 35.     ret = enif_make_tuple2(env, new_state_term, cipher_term);
> 36.     return ret;
> 37. }

Just a random guess really, but your key_bin doesn't seem to be a proper term
suitable for returning to Erlang, shoudln't you run enif_make_binary() on it?
The compiler should warn you about that too, unless ERL_NIF_TERM is something
hopelessly vague like "void *"...

Regards,
        -- Jachym
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: NIF Problem

Sverker Eriksson
Jachym Holecek wrote:

> # Travis Jensen 2011-04-28:
>  
>> [... This results in SIGBUS after returning to Erlang ...]
>>
>>  1. static ERL_NIF_TERM aes_ctr_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM
>>     argv[])
>>  2. {/* ({Key, IVec, ECount, Num}, Data) */    
>>  3.     ErlNifBinary key_bin, ivec_bin, text_bin, ecount_bin;
>>
>>    
:

>> 34.     new_state_term = enif_make_tuple4(env, key_bin, ivec2_term, ecount2_term, num2_term);
>> 35.     ret = enif_make_tuple2(env, new_state_term, cipher_term);
>> 36.     return ret;
>> 37. }
>>    
>
> Just a random guess really, but your key_bin doesn't seem to be a proper term
> suitable for returning to Erlang, shoudln't you run enif_make_binary() on it?
> The compiler should warn you about that too, unless ERL_NIF_TERM is something
> hopelessly vague like "void *"...
>
> Regards,
> -- Jachym
>
>  
Nicely spotted, Jachym.

The reason the compiler did not complain is that enif_make_tuple4 is a
macro calling variadic function enif_make_tuple(ErlNifEnv*, ...).

A patch turning enif_make_tuple# and enif_make_list# into inline
functions (if compiler supports it) would probably be a good idea. Any
takers?


/Sverker, Erlang/OTP
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: NIF Problem

Travis Jensen
In reply to this post by Jachym Holecek


On Thu, Apr 28, 2011 at 4:07 AM, Jachym Holecek <[hidden email]> wrote:

Just a random guess really, but your key_bin doesn't seem to be a proper term
suitable for returning to Erlang, shoudln't you run enif_make_binary() on it?
The compiler should warn you about that too, unless ERL_NIF_TERM is something
hopelessly vague like "void *"...

Regards,
       -- Jachym

That was a really good random guess. :)  Replaced that with the term passed in (since it doesn't change) and it worked perfectly.

Thanks for your sharp eyes!

tj
--
Travis Jensen

Read the Software Maven @ http://softwaremaven.innerbrane.com/
Read my LinkedIn profile @ http://www.linkedin.com/in/travisjensen
Read my Twitter mumblings @ http://twitter.com/SoftwareMaven
Send me email @ [hidden email]

*What kind of guy calls himself the Software Maven???*


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions