summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/ucode/types.h9
-rw-r--r--lib/socket.c19
-rw-r--r--tests/custom/99_bugs/48_use_after_free_on_iteration_insert40
-rw-r--r--types.c39
-rw-r--r--vm.c11
5 files changed, 106 insertions, 12 deletions
diff --git a/include/ucode/types.h b/include/ucode/types.h
index c0ccd38..2334029 100644
--- a/include/ucode/types.h
+++ b/include/ucode/types.h
@@ -213,7 +213,14 @@ extern uc_list_t uc_object_iterators;
typedef struct {
uc_list_t list;
- struct lh_entry *pos;
+ struct lh_table *table;
+ union {
+ struct lh_entry *pos;
+ struct {
+ const void *k;
+ unsigned long hash;
+ } kh;
+ } u;
} uc_object_iterator_t;
diff --git a/lib/socket.c b/lib/socket.c
index 63ca24f..db77188 100644
--- a/lib/socket.c
+++ b/lib/socket.c
@@ -1396,12 +1396,23 @@ static struct_t st_tpacket_auxdata = {
}
};
+struct fanout_args_local {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint16_t id;
+ uint16_t type_flags;
+#else
+ uint16_t type_flags;
+ uint16_t id;
+#endif
+ uint32_t max_num_members;
+};
+
static struct_t st_fanout_args = {
- .size = sizeof(struct fanout_args),
+ .size = sizeof(struct fanout_args_local),
.members = (member_t []){
- STRUCT_MEMBER_NP(fanout_args, id, DT_UNSIGNED),
- STRUCT_MEMBER_NP(fanout_args, type_flags, DT_UNSIGNED),
- STRUCT_MEMBER_NP(fanout_args, max_num_members, DT_UNSIGNED),
+ STRUCT_MEMBER_NP(fanout_args_local, id, DT_UNSIGNED),
+ STRUCT_MEMBER_NP(fanout_args_local, type_flags, DT_UNSIGNED),
+ STRUCT_MEMBER_NP(fanout_args_local, max_num_members, DT_UNSIGNED),
{ 0 }
}
};
diff --git a/tests/custom/99_bugs/48_use_after_free_on_iteration_insert b/tests/custom/99_bugs/48_use_after_free_on_iteration_insert
new file mode 100644
index 0000000..558f83a
--- /dev/null
+++ b/tests/custom/99_bugs/48_use_after_free_on_iteration_insert
@@ -0,0 +1,40 @@
+Ensure that adding keys to an object currently being iterated will not
+clobber active iterators pointing into that object due to a reallocation
+of the underlying hash table array.
+
+-- Testcase --
+{%
+ let obj = { '0': 0, '1': 1 };
+ let i = 2;
+
+ for (let k, v in obj) {
+ while (i < 16) {
+ obj[i] = i;
+ i++;
+ }
+ }
+
+ printf("%.J\n", obj);
+%}
+-- End --
+
+-- Expect stdout --
+{
+ "0": 0,
+ "1": 1,
+ "2": 2,
+ "3": 3,
+ "4": 4,
+ "5": 5,
+ "6": 6,
+ "7": 7,
+ "8": 8,
+ "9": 9,
+ "10": 10,
+ "11": 11,
+ "12": 12,
+ "13": 13,
+ "14": 14,
+ "15": 15
+}
+-- End --
diff --git a/types.c b/types.c
index 382e49a..9751e24 100644
--- a/types.c
+++ b/types.c
@@ -879,8 +879,8 @@ ucv_free_object_entry(struct lh_entry *entry)
uc_list_foreach(item, &uc_object_iterators) {
uc_object_iterator_t *iter = (uc_object_iterator_t *)item;
- if (iter->pos == entry)
- iter->pos = entry->next;
+ if (iter->u.pos == entry)
+ iter->u.pos = entry->next;
}
free(lh_entry_k(entry));
@@ -929,6 +929,24 @@ ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val)
existing_entry = lh_table_lookup_entry_w_hash(object->table, (const void *)key, hash);
if (existing_entry == NULL) {
+ bool rehash = (object->table->count >= object->table->size * LH_LOAD_FACTOR);
+
+ /* insert will rehash table, backup affected iterator states */
+ if (rehash) {
+ uc_list_foreach(item, &uc_object_iterators) {
+ uc_object_iterator_t *iter = (uc_object_iterator_t *)item;
+
+ if (iter->table != object->table)
+ continue;
+
+ if (iter->u.pos == NULL)
+ continue;
+
+ iter->u.kh.k = iter->u.pos->k;
+ iter->u.kh.hash = lh_get_hash(iter->table, iter->u.kh.k);
+ }
+ }
+
k = xstrdup(key);
if (lh_table_insert_w_hash(object->table, k, val, hash, 0) != 0) {
@@ -937,6 +955,23 @@ ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val)
return false;
}
+ /* restore affected iterator state pointer after rehash */
+ if (rehash) {
+ uc_list_foreach(item, &uc_object_iterators) {
+ uc_object_iterator_t *iter = (uc_object_iterator_t *)item;
+
+ if (iter->table != object->table)
+ continue;
+
+ if (iter->u.kh.k == NULL)
+ continue;
+
+ iter->u.pos = lh_table_lookup_entry_w_hash(iter->table,
+ iter->u.kh.k,
+ iter->u.kh.hash);
+ }
+ }
+
return true;
}
diff --git a/vm.c b/vm.c
index e4fe45d..3dd054b 100644
--- a/vm.c
+++ b/vm.c
@@ -2219,7 +2219,8 @@ uc_vm_object_iterator_next(uc_vm_t *vm, uc_vm_insn_t insn,
res->type = &uc_vm_object_iterator_type;
iter = res->data = (char *)res + sizeof(*res);
- iter->pos = obj->table->head;
+ iter->table = obj->table;
+ iter->u.pos = obj->table->head;
uc_list_insert(&uc_object_iterators, &iter->list);
}
@@ -2235,21 +2236,21 @@ uc_vm_object_iterator_next(uc_vm_t *vm, uc_vm_insn_t insn,
}
/* no next key */
- if (!iter->pos) {
+ if (!iter->u.pos) {
uc_list_remove(&iter->list);
return false;
}
- uc_vm_stack_push(vm, ucv_string_new(iter->pos->k));
+ uc_vm_stack_push(vm, ucv_string_new(iter->u.pos->k));
if (insn == I_NEXTKV)
- uc_vm_stack_push(vm, ucv_get((uc_value_t *)iter->pos->v));
+ uc_vm_stack_push(vm, ucv_get((uc_value_t *)iter->u.pos->v));
uc_vm_stack_push(vm, &res->header);
ucv_put(v);
- iter->pos = iter->pos->next;
+ iter->u.pos = iter->u.pos->next;
return true;
}