Skip to content

Commit

Permalink
types: resolve upvalue values in arrays and objects
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
jow- committed Nov 29, 2024
1 parent a6e0641 commit ed5ce8f
Showing 1 changed file with 37 additions and 11 deletions.
48 changes: 37 additions & 11 deletions types.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
}
Expand Down

0 comments on commit ed5ce8f

Please sign in to comment.