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