// %CopyrightBegin% // // Copyright Doug Hogan 2019. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // %CopyrightEnd% // Coccinelle script to help verify Erlang calls. // http://coccinelle.lip6.fr // https://github.com/coccinelle/coccinelle // // These work with the Erlang code because it has a rigid coding pattern. // $ spatch.opt --all-includes -sp_file check_erlang.cocci -dir . // Make sure resources are cleaned up properly in all paths. // Need 'strict' so it's also checked in error handling paths. @enif_alloc_resource@ type T; identifier CTX, L; identifier virtual.enif_alloc_resource, virtual.enif_release_resource; position p, pr; @@ T *CTX = NULL; ... if ((CTX = enif_alloc_resource(...)@p) == NULL) goto L; ... when strict, forall if (CTX) enif_release_resource(CTX)@pr; // After calling enif_alloc_binary(), you must either release it with // enif_release_binary() or transfer ownership to Erlang via enif_make_binary(). @enif_alloc_binary@ expression SZ; identifier BIN, RET, ENV, X, L; identifier TUPLE =~ "^enif_make_tuple[0-9]+$"; identifier virtual.enif_alloc_binary, virtual.enif_make_binary; identifier virtual.enif_release_binary; position pa, pm, pr; @@ // This construct is used in engine.c ( if (!enif_alloc_binary(SZ, &BIN)@pa) goto L; ... when strict, forall return ( enif_make_binary(ENV, &BIN)@pm | TUPLE(..., enif_make_binary(ENV, &BIN)@pm)@pm ); | // This is the typical way we allocate and use binaries. int X = 0; ... if (!enif_alloc_binary(SZ, &BIN)@pa) goto L; X = 1; ... when strict, forall ( RET = enif_make_binary(ENV, &BIN)@pm; X = 0; | if (X) enif_release_binary(&BIN)@pr; | return enif_make_binary(ENV, &BIN)@pm; ) ) // TODO: These don't have single checks that handle all cases. // // enif_consume_timeslice returns 1 if exhausted or else 0 // enif_has_pending_exception returns true if exception pending @erlang_check_void@ identifier FUNCVOID =~ "^(enif_mutex_destroy|enif_mutex_lock|enif_mutex_unlock|enif_rwlock_destroy|enif_rwlock_rlock|enif_rwlock_runlock|enif_rwlock_rwlock|enif_rwlock_rwunlock|enif_system_info)$"; position p; @@ FUNCVOID(...)@p; @erlang_check_null@ expression X; identifier L; identifier FUNCNULL =~ "^(enif_alloc|enif_alloc_resource|enif_dlopen|enif_dlsym|enif_make_new_binary|enif_mutex_create|enif_open_resource_type|enif_realloc|enif_rwlock_create)$"; position p; @@ ( if ((X = FUNCNULL(...)@p) == NULL) goto L; | X = FUNCNULL(...)@p; if (X == NULL) goto L; | return FUNCNULL(...)@p; ) @erlang_check_not@ identifier L; identifier FUNCNOT =~ "^(enif_alloc_binary|enif_get_int|enif_get_list_cell|enif_get_list_length|enif_get_long|enif_get_map_value|enif_get_resource|enif_get_tuple|enif_get_uint|enif_get_ulong|enif_inspect_binary|enif_inspect_iolist_as_binary|enif_is_atom|enif_is_binary|enif_is_current_process_alive|enif_is_empty_list|enif_is_list|enif_is_map|enif_is_tuple|enif_realloc_binary)$"; position p; @@ ( if (!FUNCNOT(...)@p) goto L; | return FUNCNOT(...)@p; ) @erlang_check_null_free@ expression X; identifier FUNCFREE =~ "^(enif_free|enif_free_env|enif_free_iovec|enif_release_binary|enif_release_resource)$"; position p; @@ if ( ( X | X != NULL ) ) FUNCFREE(X)@p; @erlang_check_new@ expression RET; identifier FUNCNEW =~ "^(enif_make_atom|enif_make_badarg|enif_make_binary|enif_make_int|enif_make_list|enif_make_list_from_array|enif_make_resource|enif_make_tuple|enif_raise_exception|enif_schedule_nif|enif_thread_self)$"; position p; @@ ( RET = FUNCNEW(...)@p; | return FUNCNEW(...)@p; ) // Flag any calls that aren't part of the above pattern. @enif_alloc_not_free@ identifier FUNCVOID =~ "^(enif_mutex_destroy|enif_mutex_lock|enif_mutex_unlock|enif_rwlock_destroy|enif_rwlock_rlock|enif_rwlock_runlock|enif_rwlock_rwlock|enif_rwlock_rwunlock|enif_system_info)$"; position pvoid != {erlang_check_void.p,enif_alloc_binary.pr}; identifier FUNCNULL =~ "^(enif_alloc|enif_alloc_resource|enif_dlopen|enif_dlsym|enif_make_new_binary|enif_mutex_create|enif_open_resource_type|enif_realloc|enif_rwlock_create)$"; position pnull != {erlang_check_null.p,enif_alloc_resource.p}; identifier FUNCNOT =~ "^(enif_alloc_binary|enif_get_int|enif_get_list_cell|enif_get_list_length|enif_get_long|enif_get_map_value|enif_get_resource|enif_get_tuple|enif_get_uint|enif_get_ulong|enif_inspect_binary|enif_inspect_iolist_as_binary|enif_is_atom|enif_is_binary|enif_is_current_process_alive|enif_is_empty_list|enif_is_list|enif_is_map|enif_is_tuple|enif_realloc_binary)$"; position pnot != {erlang_check_not.p,enif_alloc_binary.pa}; identifier FUNCNEW =~ "^(enif_make_atom|enif_make_badarg|enif_make_binary|enif_make_int|enif_make_list|enif_make_list_from_array|enif_make_resource|enif_make_tuple|enif_raise_exception|enif_schedule_nif|enif_thread_self)$"; position pnew != {erlang_check_new.p,enif_alloc_binary.pm}; identifier FUNCFREE =~ "^(enif_free|enif_free_env|enif_free_iovec|enif_release_binary|enif_release_resource)$"; position pfree != {enif_alloc_resource.pr,enif_alloc_binary.pr,erlang_check_null_free.p}; @@ ( * FUNCVOID(...)@pvoid | * FUNCNULL(...)@pnull | * FUNCNOT(...)@pnot | * FUNCNEW(...)@pnew | * FUNCFREE(...)@pfree )