1// %CopyrightBegin% 2// 3// Copyright Doug Hogan 2019. All Rights Reserved. 4// 5// Licensed under the Apache License, Version 2.0 (the "License"); 6// you may not use this file except in compliance with the License. 7// You may obtain a copy of the License at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, 13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// See the License for the specific language governing permissions and 15// limitations under the License. 16// 17// %CopyrightEnd% 18 19// Coccinelle script to help verify Erlang calls. 20// http://coccinelle.lip6.fr 21// https://github.com/coccinelle/coccinelle 22// 23// These work with the Erlang code because it has a rigid coding pattern. 24// $ spatch.opt --all-includes -sp_file check_erlang.cocci -dir . 25 26// Make sure resources are cleaned up properly in all paths. 27// Need 'strict' so it's also checked in error handling paths. 28@enif_alloc_resource@ 29type T; 30identifier CTX, L; 31identifier virtual.enif_alloc_resource, virtual.enif_release_resource; 32position p, pr; 33@@ 34 35 T *CTX = NULL; 36 37 ... 38 if ((CTX = enif_alloc_resource(...)@p) == NULL) 39 goto L; 40 41 ... when strict, forall 42 if (CTX) 43 enif_release_resource(CTX)@pr; 44 45 46// After calling enif_alloc_binary(), you must either release it with 47// enif_release_binary() or transfer ownership to Erlang via enif_make_binary(). 48@enif_alloc_binary@ 49expression SZ; 50identifier BIN, RET, ENV, X, L; 51identifier TUPLE =~ "^enif_make_tuple[0-9]+$"; 52identifier virtual.enif_alloc_binary, virtual.enif_make_binary; 53identifier virtual.enif_release_binary; 54position pa, pm, pr; 55@@ 56 57// This construct is used in engine.c 58( 59 if (!enif_alloc_binary(SZ, &BIN)@pa) 60 goto L; 61 62 ... when strict, forall 63 return 64( 65 enif_make_binary(ENV, &BIN)@pm 66| 67 TUPLE(..., enif_make_binary(ENV, &BIN)@pm)@pm 68); 69 70| 71// This is the typical way we allocate and use binaries. 72 int X = 0; 73 74 ... 75 if (!enif_alloc_binary(SZ, &BIN)@pa) 76 goto L; 77 X = 1; 78 79 ... when strict, forall 80( 81 RET = enif_make_binary(ENV, &BIN)@pm; 82 X = 0; 83| 84 if (X) 85 enif_release_binary(&BIN)@pr; 86| 87 return enif_make_binary(ENV, &BIN)@pm; 88) 89) 90 91// TODO: These don't have single checks that handle all cases. 92// 93// enif_consume_timeslice returns 1 if exhausted or else 0 94// enif_has_pending_exception returns true if exception pending 95 96@erlang_check_void@ 97identifier 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)$"; 98position p; 99@@ 100 101 FUNCVOID(...)@p; 102 103 104@erlang_check_null@ 105expression X; 106identifier L; 107identifier 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)$"; 108position p; 109@@ 110 111( 112 if ((X = FUNCNULL(...)@p) == NULL) 113 goto L; 114| 115 X = FUNCNULL(...)@p; 116 if (X == NULL) 117 goto L; 118| 119 return FUNCNULL(...)@p; 120) 121 122 123@erlang_check_not@ 124identifier L; 125identifier 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)$"; 126position p; 127@@ 128 129( 130 if (!FUNCNOT(...)@p) 131 goto L; 132| 133 return FUNCNOT(...)@p; 134) 135 136 137@erlang_check_null_free@ 138expression X; 139identifier FUNCFREE =~ "^(enif_free|enif_free_env|enif_free_iovec|enif_release_binary|enif_release_resource)$"; 140position p; 141@@ 142 143 if ( 144( 145 X 146| 147 X != NULL 148) 149 ) 150 FUNCFREE(X)@p; 151 152 153@erlang_check_new@ 154expression RET; 155identifier 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)$"; 156position p; 157@@ 158 159( 160 RET = FUNCNEW(...)@p; 161| 162 return FUNCNEW(...)@p; 163) 164 165 166// Flag any calls that aren't part of the above pattern. 167@enif_alloc_not_free@ 168 169identifier 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)$"; 170position pvoid != {erlang_check_void.p,enif_alloc_binary.pr}; 171 172identifier 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)$"; 173position pnull != {erlang_check_null.p,enif_alloc_resource.p}; 174 175identifier 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)$"; 176position pnot != {erlang_check_not.p,enif_alloc_binary.pa}; 177 178identifier 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)$"; 179position pnew != {erlang_check_new.p,enif_alloc_binary.pm}; 180 181identifier FUNCFREE =~ "^(enif_free|enif_free_env|enif_free_iovec|enif_release_binary|enif_release_resource)$"; 182position pfree != {enif_alloc_resource.pr,enif_alloc_binary.pr,erlang_check_null_free.p}; 183 184@@ 185 186( 187* FUNCVOID(...)@pvoid 188| 189* FUNCNULL(...)@pnull 190| 191* FUNCNOT(...)@pnot 192| 193* FUNCNEW(...)@pnew 194| 195* FUNCFREE(...)@pfree 196) 197