diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2017-05-19 19:40:24 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2017-05-19 19:40:24 -0700 |
commit | a047e7f97db2c9947d0b017eeb8de126e656e1a4 (patch) | |
tree | e63e5a28cb7a77b8c97309dc7033dc010ceeb941 | |
parent | 60793497c9d4c6f832c10408a9f390a1f2487a9a (diff) | |
download | txr-a047e7f97db2c9947d0b017eeb8de126e656e1a4.tar.gz txr-a047e7f97db2c9947d0b017eeb8de126e656e1a4.tar.bz2 txr-a047e7f97db2c9947d0b017eeb8de126e656e1a4.zip |
ffi: release return value when aborting callback.
Continuing on the topic of the previous commit, what if in a
callback an error occurs in the put operation that produces
the return value? In that case we end up clobbering with
the abort_retval or nulling out with zero bytes, either way
potentially leaking memory.
* ffi.c (ffi_closure_dispatch_safe): If the return value type
has release semantics, then as the very first thing, null out
the return value buffer. Then later if a dynamic control
transfer is intercepted, invoke the release semantics before
doing anything to the return value buffer.
-rw-r--r-- | ffi.c | 12 |
1 files changed, 8 insertions, 4 deletions
@@ -2181,11 +2181,14 @@ static void ffi_closure_dispatch_safe(ffi_cif *cif, void *cret, struct txr_ffi_call_desc *tfcd = tfcl->tfcd; val types = tfcd->argtypes; val rtype = tfcd->rettype; - struct txr_ffi_type *volatile rtft = 0; - val retval = nil; + struct txr_ffi_type *rtft = ffi_type_struct(rtype); + volatile val retval = nao; int out_pass_needed = 0; uw_frame_t cont_guard; + if (rtft->release != 0) + memset(cret, 0, rtft->size); + uw_push_guard(&cont_guard, 0); uw_simple_catch_begin; @@ -2193,7 +2196,6 @@ static void ffi_closure_dispatch_safe(ffi_cif *cif, void *cret, { args_decl(args, tfcl->nparam); args_decl(args_cp, tfcl->nparam); - rtft = ffi_type_struct(rtype); for (i = 0; i < nargs; i++) { val type = pop(&types); @@ -2223,7 +2225,9 @@ static void ffi_closure_dispatch_safe(ffi_cif *cif, void *cret, uw_unwind { s_exit_point = uw_curr_exit_point; - if (s_exit_point && rtft != 0) { + if (s_exit_point) { + if (rtft->release != 0 && retval != nao) + rtft->release(rtft, retval, convert(mem_t *, cret)); if (!tfcl->abort_retval) memset(cret, 0, rtft->size); else |