diff options
Diffstat (limited to 'erts/emulator/beam/erl_nif.c')
-rw-r--r-- | erts/emulator/beam/erl_nif.c | 166 |
1 files changed, 119 insertions, 47 deletions
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 585a6c1fdf..cee4df72a2 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -51,6 +51,16 @@ struct erl_module_nif { int is_orphan; /* if erlang module has been purged */ }; +#ifdef DEBUG +# define READONLY_CHECK +#endif +#ifdef READONLY_CHECK +# define ADD_READONLY_CHECK(ENV,PTR,SIZE) add_readonly_check(ENV,PTR,SIZE) +static void add_readonly_check(ErlNifEnv*, unsigned char* ptr, unsigned sz); +#else +# define ADD_READONLY_CHECK(ENV,PTR,SIZE) ((void)0) +#endif + #define MIN_HEAP_FRAG_SZ 200 static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp); @@ -106,6 +116,13 @@ static void pre_nif_noproc(ErlNifEnv* env, struct erl_module_nif* mod_nif) env->tmp_obj_list = NULL; } +/* Temporary object header, auto-deallocated when NIF returns. */ +struct enif_tmp_obj_t { + struct enif_tmp_obj_t* next; + void (*dtor)(struct enif_tmp_obj_t*); + /*char data[];*/ +}; + static ERTS_INLINE void free_tmp_objs(ErlNifEnv* env) { while (env->tmp_obj_list != NULL) { @@ -176,7 +193,6 @@ static void disable_halloc(ErlNifEnv* env) } } - void* enif_priv_data(ErlNifEnv* env) { return env->mod_nif->priv_data; @@ -234,7 +250,7 @@ int enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term) static void aligned_binary_dtor(struct enif_tmp_obj_t* obj) { - erts_free_aligned_binary_bytes((byte*)obj); + erts_free_aligned_binary_bytes_extra((byte*)obj,ERTS_ALC_T_TMP); } int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin) @@ -244,7 +260,7 @@ int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin) byte* raw_ptr; }u; u.tmp = NULL; - bin->data = erts_get_aligned_binary_bytes_extra(bin_term, &u.raw_ptr, + bin->data = erts_get_aligned_binary_bytes_extra(bin_term, &u.raw_ptr, ERTS_ALC_T_TMP, sizeof(struct enif_tmp_obj_t)); if (bin->data == NULL) { return 0; @@ -257,6 +273,7 @@ int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin) bin->bin_term = bin_term; bin->size = binary_size(bin_term); bin->ref_bin = NULL; + ADD_READONLY_CHECK(env, bin->data, bin->size); return 1; } @@ -293,6 +310,7 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin) bin->bin_term = THE_NON_VALUE; bin->ref_bin = NULL; io_list_to_buf(term, (char*) bin->data, sz); + ADD_READONLY_CHECK(env, bin->data, bin->size); return 1; } @@ -357,6 +375,15 @@ void enif_release_binary(ErlNifEnv* env, ErlNifBinary* bin) #endif } +unsigned char* enif_make_new_binary(ErlNifEnv* env, unsigned size, + ERL_NIF_TERM* termp) +{ + enable_halloc(env); + *termp = new_binary(env->proc, NULL, size); + disable_halloc(env); + return binary_bytes(*termp); +} + int enif_is_identical(ErlNifEnv* env, Eterm lhs, Eterm rhs) { return EQ(lhs,rhs); @@ -496,9 +523,9 @@ int enif_get_atom(ErlNifEnv* env, Eterm atom, char* buf, unsigned len) int enif_get_int(ErlNifEnv* env, Eterm term, int* ip) { -#if SIZEOF_INT == SIZEOF_VOID_P +#if SIZEOF_INT == ERTS_SIZEOF_ETERM return term_to_Sint(term, (Sint*)ip); -#elif SIZEOF_LONG == SIZEOF_VOID_P +#elif SIZEOF_LONG == ERTS_SIZEOF_ETERM Sint i; if (!term_to_Sint(term, &i) || i < INT_MIN || i > INT_MAX) { return 0; @@ -512,9 +539,9 @@ int enif_get_int(ErlNifEnv* env, Eterm term, int* ip) int enif_get_uint(ErlNifEnv* env, Eterm term, unsigned* ip) { -#if SIZEOF_INT == SIZEOF_VOID_P +#if SIZEOF_INT == ERTS_SIZEOF_ETERM return term_to_Uint(term, (Uint*)ip); -#elif SIZEOF_LONG == SIZEOF_VOID_P +#elif SIZEOF_LONG == ERTS_SIZEOF_ETERM Uint i; if (!term_to_Uint(term, &i) || i > UINT_MAX) { return 0; @@ -526,8 +553,13 @@ int enif_get_uint(ErlNifEnv* env, Eterm term, unsigned* ip) int enif_get_long(ErlNifEnv* env, Eterm term, long* ip) { -#if SIZEOF_LONG == SIZEOF_VOID_P +#if SIZEOF_LONG == ERTS_SIZEOF_ETERM return term_to_Sint(term, ip); +#elif SIZEOF_INT == ERTS_SIZEOF_ETERM + Uint u; + term_to_Sint(term, u); + *ip = (long) u; + return 1; #else # error Unknown long word size #endif @@ -535,8 +567,14 @@ int enif_get_long(ErlNifEnv* env, Eterm term, long* ip) int enif_get_ulong(ErlNifEnv* env, Eterm term, unsigned long* ip) { -#if SIZEOF_LONG == SIZEOF_VOID_P +#if SIZEOF_LONG == ERTS_SIZEOF_ETERM return term_to_Uint(term, ip); +#elif SIZEOF_INT == ERTS_SIZEOF_ETERM + Uint u; + int r; + r = term_to_Uint(term, &u); + *ip = (unsigned long) u; + return r; #else # error Unknown long word size #endif @@ -565,27 +603,24 @@ int enif_get_list_cell(ErlNifEnv* env, Eterm term, Eterm* head, Eterm* tail) ERL_NIF_TERM enif_make_int(ErlNifEnv* env, int i) { -#if SIZEOF_INT == SIZEOF_VOID_P +#if SIZEOF_INT == ERTS_SIZEOF_ETERM return IS_SSMALL(i) ? make_small(i) : small_to_big(i,alloc_heap(env,2)); -#elif SIZEOF_LONG == SIZEOF_VOID_P +#elif SIZEOF_LONG == ERTS_SIZEOF_ETERM return make_small(i); #endif } ERL_NIF_TERM enif_make_uint(ErlNifEnv* env, unsigned i) { -#if SIZEOF_INT == SIZEOF_VOID_P +#if SIZEOF_INT == ERTS_SIZEOF_ETERM return IS_USMALL(0,i) ? make_small(i) : uint_to_big(i,alloc_heap(env,2)); -#elif SIZEOF_LONG == SIZEOF_VOID_P +#elif SIZEOF_LONG == ERTS_SIZEOF_ETERM return make_small(i); #endif } ERL_NIF_TERM enif_make_long(ErlNifEnv* env, long i) { -#if SIZEOF_LONG != SIZEOF_VOID_P -# error Unknown long word size -#endif return IS_SSMALL(i) ? make_small(i) : small_to_big(i, alloc_heap(env,2)); } @@ -967,37 +1002,22 @@ unsigned enif_sizeof_resource(ErlNifEnv* env, void* obj) ***************************************************************************/ -static Uint** get_func_pp(Eterm* mod_code, Eterm f_atom, unsigned arity) +static BeamInstr** get_func_pp(BeamInstr* mod_code, Eterm f_atom, unsigned arity) { int n = (int) mod_code[MI_NUM_FUNCTIONS]; int j; for (j = 0; j < n; ++j) { - Uint* code_ptr = (Uint*) mod_code[MI_FUNCTIONS+j]; - ASSERT(code_ptr[0] == (Uint) BeamOp(op_i_func_info_IaaI)); + BeamInstr* code_ptr = (BeamInstr*) mod_code[MI_FUNCTIONS+j]; + ASSERT(code_ptr[0] == (BeamInstr) BeamOp(op_i_func_info_IaaI)); if (f_atom == ((Eterm) code_ptr[3]) && arity == ((unsigned) code_ptr[4])) { - return (Uint**) &mod_code[MI_FUNCTIONS+j]; + return (BeamInstr**) &mod_code[MI_FUNCTIONS+j]; } } return NULL; } -/*static void refresh_cached_nif_data(Eterm* mod_code, - struct erl_module_nif* mod_nif) -{ - int i; - for (i=0; i < mod_nif->entry->num_of_funcs; i++) { - Eterm f_atom; - ErlNifFunc* func = &mod_nif->entry->funcs[i]; - Uint* code_ptr; - - erts_atom_get(func->name, sys_strlen(func->name), &f_atom); - code_ptr = *get_func_pp(mod_code, f_atom, func->arity); - code_ptr[5+2] = (Uint) mod_nif->priv_data; - } -}*/ - static Eterm mkatom(const char *str) { return am_atom_put(str, sys_strlen(str)); @@ -1089,7 +1109,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) static const char bad_lib[] = "bad_lib"; static const char reload[] = "reload"; static const char upgrade[] = "upgrade"; - char lib_name[256]; /* BUGBUG: Max-length? */ + char* lib_name = NULL; void* handle = NULL; void* init_func; ErlNifEntry* entry = NULL; @@ -1098,15 +1118,20 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) Module* mod; Eterm mod_atom; Eterm f_atom; - Eterm* caller; + BeamInstr* caller; ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT; Eterm ret = am_ok; int veto; struct erl_module_nif* lib = NULL; - len = intlist_to_buf(BIF_ARG_1, lib_name, sizeof(lib_name)-1); - if (len < 1) { - /*erts_fprintf(stderr, "Invalid library path name '%T'\r\n", BIF_ARG_1);*/ + len = list_length(BIF_ARG_1); + if (len < 0) { + BIF_ERROR(BIF_P, BADARG); + } + lib_name = (char *) erts_alloc(ERTS_ALC_T_TMP, len + 1); + + if (intlist_to_buf(BIF_ARG_1, lib_name, len) != len) { + erts_free(ERTS_ALC_T_TMP, lib_name); BIF_ERROR(BIF_P, BADARG); } lib_name[len] = '\0'; @@ -1165,7 +1190,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) /*erts_fprintf(stderr, "Found module %T\r\n", mod_atom);*/ for (i=0; i < entry->num_of_funcs && ret==am_ok; i++) { - Uint** code_pp; + BeamInstr** code_pp; ErlNifFunc* f = &entry->funcs[i]; if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom) || (code_pp = get_func_pp(mod->code, f_atom, f->arity))==NULL) { @@ -1268,22 +1293,22 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) mod->nif = lib; for (i=0; i < entry->num_of_funcs; i++) { - Uint* code_ptr; + BeamInstr* code_ptr; erts_atom_get(entry->funcs[i].name, sys_strlen(entry->funcs[i].name), &f_atom); code_ptr = *get_func_pp(mod->code, f_atom, entry->funcs[i].arity); if (code_ptr[1] == 0) { - code_ptr[5+0] = (Uint) BeamOp(op_call_nif); + code_ptr[5+0] = (BeamInstr) BeamOp(op_call_nif); } else { /* Function traced, patch the original instruction word */ BpData* bp = (BpData*) code_ptr[1]; - bp->orig_instr = (Uint) BeamOp(op_call_nif); + bp->orig_instr = (BeamInstr) BeamOp(op_call_nif); } - code_ptr[5+1] = (Uint) entry->funcs[i].fptr; - code_ptr[5+2] = (Uint) lib; + code_ptr[5+1] = (BeamInstr) entry->funcs[i].fptr; + code_ptr[5+2] = (BeamInstr) lib; } } - else { + else { error: ASSERT(ret != am_ok); if (lib != NULL) { @@ -1297,6 +1322,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) erts_smp_release_system(); erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_free(ERTS_ALC_T_TMP, lib_name); BIF_RET(ret); } @@ -1350,3 +1376,49 @@ void erl_nif_init() resource_type_list.name[0] = '\0'; } +#ifdef READONLY_CHECK +/* Use checksums to assert that NIFs do not write into inspected binaries +*/ +static void readonly_check_dtor(struct enif_tmp_obj_t*); +static unsigned calc_checksum(unsigned char* ptr, unsigned size); + +struct readonly_check_t +{ + struct enif_tmp_obj_t hdr; + unsigned char* ptr; + unsigned size; + unsigned checksum; +}; +static void add_readonly_check(ErlNifEnv* env, unsigned char* ptr, unsigned sz) +{ + struct readonly_check_t* obj = erts_alloc(ERTS_ALC_T_TMP, + sizeof(struct readonly_check_t)); + obj->hdr.next = env->tmp_obj_list; + env->tmp_obj_list = &obj->hdr; + obj->hdr.dtor = &readonly_check_dtor; + obj->ptr = ptr; + obj->size = sz; + obj->checksum = calc_checksum(ptr, sz); +} +static void readonly_check_dtor(struct enif_tmp_obj_t* o) +{ + struct readonly_check_t* obj = (struct readonly_check_t*) o; + unsigned chksum = calc_checksum(obj->ptr, obj->size); + if (chksum != obj->checksum) { + fprintf(stderr, "\r\nReadonly data written by NIF, checksums differ" + " %x != %x\r\nABORTING\r\n", chksum, obj->checksum); + abort(); + } + erts_free(ERTS_ALC_T_TMP, obj); +} +static unsigned calc_checksum(unsigned char* ptr, unsigned size) +{ + unsigned i, sum = 0; + for (i=0; i<size; i++) { + sum ^= ptr[i] << ((i % 4)*8); + } + return sum; +} + +#endif /* READONLY_CHECK */ + |