From 4664ad3bfb3224b212fed61a730f2ac83e2cd777 Mon Sep 17 00:00:00 2001 From: Slendi Date: Fri, 1 Aug 2025 08:06:25 +0300 Subject: [PATCH] Add lazy evaluation, and introduce use-after-free Signed-off-by: Slendi --- programs/dcfgq.c | 29 ++++++----- src/dcfg.c | 131 ++++++++++++++++++++++++----------------------- 2 files changed, 82 insertions(+), 78 deletions(-) diff --git a/programs/dcfgq.c b/programs/dcfgq.c index 27ca1c2..c918a55 100644 --- a/programs/dcfgq.c +++ b/programs/dcfgq.c @@ -105,39 +105,40 @@ int main(int argc, char **argv) } dcfg_Value *cur = evaled; - for (size_t i = 0; i < n; i++) { + for (size_t i = 0; i < n; ++i) { Token *t = &toks[i]; dcfg_Value *next = NULL; if (t->type == T_FIELD) { if (!dcfg_Value_get_object_field_ex(cur, t->v.field, &next, true)) { - fprintf(stderr, "no such field\n"); - dcfg_destroy(evaled); - dcfg_destroy(root); - dcfg_destroy_instance(inst); - return 1; + fputs("no such field\n", stderr); + goto fail; } } else { if (!dcfg_Value_get_array_item_ex(cur, t->v.index, &next, true)) { - fprintf(stderr, "index out of bounds\n"); - dcfg_destroy(evaled); - dcfg_destroy(root); - dcfg_destroy_instance(inst); - return 1; + fputs("index out of bounds\n", stderr); + goto fail; } } - if (cur != evaled) + if (cur != evaled) { dcfg_destroy(cur); - + } cur = next; } print_value(cur); - if (cur != evaled) + if (cur != evaled) { dcfg_destroy(cur); + } dcfg_destroy(evaled); dcfg_destroy(root); dcfg_destroy_instance(inst); return 0; + +fail: + dcfg_destroy(evaled); + dcfg_destroy(root); + dcfg_destroy_instance(inst); + return 1; } diff --git a/src/dcfg.c b/src/dcfg.c index 321baf7..2fb2ecd 100644 --- a/src/dcfg.c +++ b/src/dcfg.c @@ -260,6 +260,7 @@ typedef struct { StringView k; Value *v; bool key_allocated; + bool owned; } ValueObjectEntry; typedef struct { @@ -268,6 +269,7 @@ typedef struct { typedef struct { Value **valuev; + bool *ownedv; } ValueArray; typedef struct { @@ -310,6 +312,8 @@ struct dcfg_Value { ValueMemberAccess ma; ValueFunctionCall c; } v; + + bool freed; }; typedef struct Environment { @@ -1355,6 +1359,7 @@ Value *ast_to_value(dcfg_Instance *instance, AST *root) } else if (root->kind == ASTKind_Array) { value->type = dcfg_ValueType_Array; value->v.a.valuev = vector_create(); + value->v.a.ownedv = vector_create(); for (size_t i = 0; i < vector_size(root->v.a.childv); i++) { Value *v = ast_to_value(instance, root->v.a.childv[i]); if (!v) { @@ -1366,6 +1371,7 @@ Value *ast_to_value(dcfg_Instance *instance, AST *root) return NULL; } vector_add(&value->v.a.valuev, Value *, v); + vector_add(&value->v.a.ownedv, bool, true); } } else if (root->kind == ASTKind_Function) { value->type = dcfg_ValueType_Function; @@ -1527,38 +1533,55 @@ void dcfg_destroy(dcfg_Value *value) switch (value->type) { case dcfg_ValueType_Object: for (size_t i = 0; i < vector_size(value->v.o.entryv); i++) { - dcfg_destroy(value->v.o.entryv[i].v); + if (value->v.o.entryv[i].owned) { + dcfg_destroy(value->v.o.entryv[i].v); + value->v.o.entryv[i].v = NULL; + } if (value->v.o.entryv[i].key_allocated) { value->instance->free((void *)value->v.o.entryv[i].k.data); + value->v.o.entryv[i].k.data = NULL; } } vector_free(value->v.o.entryv); + value->v.o.entryv = NULL; break; case dcfg_ValueType_Array: for (size_t i = 0; i < vector_size(value->v.a.valuev); i++) { - dcfg_destroy(value->v.a.valuev[i]); + if (value->v.a.ownedv[i]) { + dcfg_destroy(value->v.a.valuev[i]); + value->v.a.valuev[i] = NULL; + } } vector_free(value->v.a.valuev); + value->v.a.valuev = NULL; + vector_free(value->v.a.ownedv); + value->v.a.ownedv = NULL; break; case dcfg_ValueType_Function: if (!value->v.f.is_builtin) { dcfg_destroy(value->v.f.v.f.body); + value->v.f.v.f.body = NULL; vector_free(value->v.f.v.f.argv); + value->v.f.v.f.argv = NULL; } break; case dcfg_ValueType_FunctionCall: dcfg_destroy(value->v.c.function); + value->v.c.function = NULL; for (size_t i = 0; i < vector_size(value->v.c.argv); i++) { dcfg_destroy(value->v.c.argv[i]); + value->v.c.argv[i] = NULL; } vector_free(value->v.c.argv); + value->v.c.argv = NULL; break; case dcfg_ValueType_MemberAccess: vector_free(value->v.ma.accessv); + value->v.ma.accessv = NULL; break; default: @@ -1712,38 +1735,46 @@ dcfg_ValueType dcfg_Value_type_ex(dcfg_Value *value, bool evaluate) return value->type; } -bool dcfg_Value_get_object_field_ex(dcfg_Value *value, dcfg_StringView key, - dcfg_Value **out_value, bool evaluate) +bool dcfg_Value_get_object_field_ex( + Value *obj, StringView key, Value **out, bool evaluate) { - if (!value || value->type != dcfg_ValueType_Object) + if (!obj || obj->type != dcfg_ValueType_Object) return false; - ValueObject *obj = &value->v.o; - for (size_t i = 0; i < vector_size(obj->entryv); ++i) { - ValueObjectEntry *entry = &obj->entryv[i]; - if (sv_eq(entry->k, key)) { - *out_value = entry->v; - if (evaluate) - dcfg_Value_evaluate(*out_value, out_value); - return true; + for (size_t i = 0; i < vector_size(obj->v.o.entryv); ++i) { + ValueObjectEntry *e = &obj->v.o.entryv[i]; + if (!sv_eq(e->k, key)) + continue; + + *out = e->v; + + if (evaluate) { + if (!dcfg_Value_evaluate(*out, out)) + return false; + e->v = *out; + e->owned = true; } + return true; } return false; } bool dcfg_Value_get_array_item_ex( - dcfg_Value *value, size_t index, dcfg_Value **out_value, bool evaluate) + Value *arr, size_t idx, Value **out, bool evaluate) { - if (!value || value->type != dcfg_ValueType_Array) + if (!arr || arr->type != dcfg_ValueType_Array) + return false; + if (idx >= vector_size(arr->v.a.valuev)) return false; - ValueArray *arr = &value->v.a; - if (index >= vector_size(arr->valuev)) - return false; + *out = arr->v.a.valuev[idx]; - *out_value = arr->valuev[index]; - if (evaluate) - dcfg_Value_evaluate(*out_value, out_value); + if (evaluate) { + if (!dcfg_Value_evaluate(*out, out)) + return false; + arr->v.a.valuev[idx] = *out; + arr->v.a.ownedv[idx] = true; + } return true; } @@ -1880,45 +1911,30 @@ bool dcfg_Value_evaluate_in_env( v->type = dcfg_ValueType_Path; v->v.p = value->v.p; } else if (value->type == dcfg_ValueType_Object) { + v->type = dcfg_ValueType_Object; v->v.o.entryv = vector_create(); - for (size_t i = 0; i < vector_size(value->v.o.entryv); i++) { + for (size_t i = 0; i < vector_size(value->v.o.entryv); ++i) { ValueObjectEntry *e = &value->v.o.entryv[i]; - Value *new_v; - bool res = dcfg_Value_evaluate_in_env(e->v, frame, &new_v); - if (!res) { - ret = false; - break; - } ValueObjectEntry ne = { .k = e->k, - .v = new_v, - .key_allocated = true, + .v = e->v, + .key_allocated = false, + .owned = false, }; - ne.k.data = value->instance->alloc(ne.k.size + 1); - memcpy((void *)ne.k.data, e->k.data, ne.k.size); - ((char *)ne.k.data)[ne.k.size] = '\0'; vector_add(&v->v.o.entryv, ValueObjectEntry, ne); } } else if (value->type == dcfg_ValueType_Array) { v->type = dcfg_ValueType_Array; v->v.a.valuev = vector_create(); - for (size_t i = 0; i < vector_size(value->v.a.valuev); i++) { - Value *val = NULL; - bool res - = dcfg_Value_evaluate_in_env(value->v.a.valuev[i], frame, &val); - if (!res) { - ret = false; - } - vector_add(&v->v.a.valuev, Value *, val); + v->v.a.ownedv = vector_create(); + for (size_t i = 0; i < vector_size(value->v.a.valuev); ++i) { + vector_add(&v->v.a.valuev, Value *, value->v.a.valuev[i]); + vector_add(&v->v.a.ownedv[i], bool, false); } } else if (value->type == dcfg_ValueType_Function) { - Value *out_value_prev = *out_value; - bool res = dcfg_call_function(value, NULL, 0, out_value); - if (!res) { - dcfg_destroy(out_value_prev); - ret = false; - } + v->type = dcfg_ValueType_Function; + v->v.f = value->v.f; } else if (value->type == dcfg_ValueType_MemberAccess) { if (!frame) { ret = false; @@ -1934,21 +1950,9 @@ bool dcfg_Value_evaluate_in_env( } } } else if (value->type == dcfg_ValueType_FunctionCall) { - Value *function; - bool res - = dcfg_Value_evaluate_in_env(value->v.c.function, frame, &function); - if (!res || function->type != dcfg_ValueType_Function) { - ret = false; - } else { - Value *out_value_prev = *out_value; - bool res = dcfg_call_function(function, value->v.c.argv, - vector_size(value->v.c.argv), out_value); - if (!res) { - ret = false; - } else { - dcfg_destroy(out_value_prev); - } - } + v->type = dcfg_ValueType_FunctionCall; + v->v.c.function = value->v.c.function; + v->v.c.argv = value->v.c.argv; } else { assert(0 && "Invalid value type"); } @@ -2078,8 +2082,7 @@ bool dcfg_Value_evaluate_toplevel(dcfg_Value *top, dcfg_Value **out_value, Value *argv[1] = { lib }; bool ok = dcfg_call_function(top, argv, 1, out_value); - vector_free(lib->v.o.entryv); - inst->free(lib); + dcfg_destroy(lib); return ok; }