diff options
author | Jo-Philipp Wich <jo@mein.io> | 2024-11-29 16:36:53 +0100 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2024-11-29 16:45:07 +0100 |
commit | ed5ce8f490188f3c528c7dd5db09ec0470377283 (patch) | |
tree | a9e37aca9c94fa5ec3ed90c589d6881bf6dad173 /types.c | |
parent | a6e06417785288f66c990e828cccbf9fb9836ba7 (diff) |
types: resolve upvalue values in arrays and objects
Some objects, such as wildcard module import namespace dictionaries may
contain upvalue type values. Extend `ucv_key_get()` to transparently
resolve such values before returning them to the caller in order to
avoid increasing the refcount of the upvalue itself, leading to a
memory leak later on when the VM indirectly dereferences it on upon
`uc_vm_stack_push()`, loosing the upvalue object reference itself
in the process.
This long standing leak was discovered while fixing another upvalue
related module import quirk.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'types.c')
-rw-r--r-- | types.c | 48 |
1 files changed, 37 insertions, 11 deletions
@@ -2237,8 +2237,9 @@ uc_value_t * ucv_key_get(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key) { uc_value_t *o, *v = NULL; + bool found = false; + uc_upvalref_t *ref; int64_t idx; - bool found; char *k; if (ucv_type(scope) == UC_ARRAY) { @@ -2247,23 +2248,48 @@ ucv_key_get(uc_vm_t *vm, uc_value_t *scope, uc_value_t *key) if (idx < 0 && idx > INT64_MIN && (uint64_t)llabs(idx) <= ucv_array_length(scope)) idx += ucv_array_length(scope); - if (idx >= 0 && (uint64_t)idx < ucv_array_length(scope)) - return ucv_get(ucv_array_get(scope, idx)); + if (idx >= 0 && (uint64_t)idx < ucv_array_length(scope)) { + v = ucv_array_get(scope, idx); + found = true; + } } - k = ucv_key_to_string(vm, key); + if (!found) { + k = ucv_key_to_string(vm, key); + + for (o = scope; o; o = ucv_prototype_get(o)) { + if (ucv_type(o) != UC_OBJECT) + continue; - for (o = scope; o; o = ucv_prototype_get(o)) { - if (ucv_type(o) != UC_OBJECT) - continue; + v = ucv_object_get(o, k ? k : ucv_string_get(key), &found); - v = ucv_object_get(o, k ? k : ucv_string_get(key), &found); + if (found) + break; + } - if (found) - break; + free(k); } - free(k); + /* Handle upvalue values in objects; under some specific circumstances + objects may contain upvalues, this primarily happens with wildcard module + import namespace dictionaries. */ +#ifdef __clang_analyzer__ + /* Clang static analyzer does not understand that ucv_type(NULL) can't + * possibly yield UC_UPVALUE. Nudge it. */ + if (v != NULL && ucv_type(v) == UC_UPVALUE) +#else + if (ucv_type(v) == UC_UPVALUE) +#endif + { + ref = (uc_upvalref_t *)v; + + if (ref->closed) + return ucv_get(ref->value); + else if (vm) + return ucv_get(vm->stack.entries[ref->slot]); + else + return NULL; + } return ucv_get(v); } |