Add expr_clone, type_clone; call from value_clone
- This fixes several memory errors in trace_clone and hello_vfork test
cases.
diff --git a/expr.c b/expr.c
index 552a53c..4059a32 100644
--- a/expr.c
+++ b/expr.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -171,6 +171,85 @@
abort();
}
+static int
+expr_alloc_and_clone(struct expr_node **retpp, struct expr_node *node, int own)
+{
+ *retpp = node;
+ if (own) {
+ *retpp = malloc(sizeof **retpp);
+ if (*retpp == NULL || expr_clone(*retpp, node) < 0) {
+ free(*retpp);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+expr_clone(struct expr_node *retp, const struct expr_node *node)
+{
+ *retp = *node;
+
+ switch (node->kind) {
+ struct expr_node *nlhs;
+ struct expr_node *nrhs;
+
+ case EXPR_OP_ARGNO:
+ case EXPR_OP_SELF:
+ return 0;
+
+ case EXPR_OP_CONST:
+ return value_clone(&retp->u.value, &node->u.value);
+
+ case EXPR_OP_NAMED:
+ if (node->u.name.own
+ && (retp->u.name.s = strdup(node->u.name.s)) == NULL)
+ return -1;
+ return 0;
+
+ case EXPR_OP_INDEX:
+ if (expr_alloc_and_clone(&nlhs, node->lhs, node->own_lhs) < 0)
+ return -1;
+
+ if (expr_alloc_and_clone(&nrhs, node->u.node.n,
+ node->u.node.own) < 0) {
+ if (nlhs != node->lhs) {
+ expr_destroy(nlhs);
+ free(nlhs);
+ }
+ return -1;
+ }
+
+ retp->lhs = nlhs;
+ retp->u.node.n = nrhs;
+ return 0;
+
+ case EXPR_OP_CALL2:
+ if (expr_alloc_and_clone(&nrhs, node->u.call.rhs,
+ node->u.call.own_rhs) < 0)
+ return -1;
+ retp->u.call.rhs = nrhs;
+ /* Fall through. */
+
+ case EXPR_OP_UP:
+ case EXPR_OP_CALL1:
+ if (expr_alloc_and_clone(&nlhs, node->lhs, node->own_lhs) < 0) {
+ if (node->kind == EXPR_OP_CALL2
+ && node->u.call.own_rhs) {
+ expr_destroy(nrhs);
+ free(nrhs);
+ return -1;
+ }
+ }
+
+ retp->lhs = nlhs;
+ return 0;
+ }
+
+ assert(!"Invalid value of node kind");
+ abort();
+}
+
int
expr_is_compile_constant(struct expr_node *node)
{
diff --git a/expr.h b/expr.h
index 53b75b7..ef49c25 100644
--- a/expr.h
+++ b/expr.h
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -125,6 +125,10 @@
/* Release the data inside NODE. Doesn't free NODE itself. */
void expr_destroy(struct expr_node *node);
+/* Copy expression NODE into the area pointed to by RETP. Return 0 on
+ * success or a negative value on failure. */
+int expr_clone(struct expr_node *retp, const struct expr_node *node);
+
/* Evaluate the expression NODE in context of VALUE. ARGUMENTS is a
* dictionary of named and numbered values that NODE may use. Returns
* 0 in case of success or a negative value on error. CONTEXT and
diff --git a/type.c b/type.c
index d5bc98f..458da4b 100644
--- a/type.c
+++ b/type.c
@@ -249,6 +249,95 @@
}
}
+static int
+type_alloc_and_clone(struct arg_type_info **retpp,
+ struct arg_type_info *info, int own)
+{
+ *retpp = info;
+ if (own) {
+ *retpp = malloc(sizeof **retpp);
+ if (*retpp == NULL || type_clone(*retpp, info) < 0) {
+ free(*retpp);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static enum callback_status
+clone_struct_add_field(const struct struct_field *field, void *data)
+{
+ struct arg_type_info *retp = data;
+ struct arg_type_info *info;
+ if (type_alloc_and_clone(&info, field->info, field->own_info) < 0) {
+ fail:
+ if (info != field->info)
+ free(info);
+ return CBS_STOP;
+ }
+
+ if (type_struct_add(retp, info, field->own_info) < 0) {
+ if (field->own_info)
+ type_destroy(info);
+ goto fail;
+ }
+
+ return CBS_CONT;
+}
+
+int
+type_clone(struct arg_type_info *retp, const struct arg_type_info *info)
+{
+ switch (info->type) {
+ case ARGTYPE_STRUCT:
+ type_init_struct(retp);
+ if (VECT_EACH_CST(&info->u.entries, struct struct_field, NULL,
+ clone_struct_add_field, retp) != NULL) {
+ type_destroy(retp);
+ return -1;
+ }
+ break;
+
+ case ARGTYPE_ARRAY:;
+ struct arg_type_info *elt_type;
+ if (type_alloc_and_clone(&elt_type, info->u.array_info.elt_type,
+ info->u.array_info.own_info) < 0)
+ return -1;
+
+ assert(!info->u.array_info.own_length); // XXXXXXX
+ type_init_array(retp, elt_type, info->u.array_info.own_info,
+ info->u.array_info.length,
+ info->u.array_info.own_length);
+ break;
+
+ case ARGTYPE_POINTER:;
+ struct arg_type_info *ninfo;
+ if (type_alloc_and_clone(&ninfo, info->u.ptr_info.info,
+ info->u.ptr_info.own_info) < 0)
+ return -1;
+ type_init_pointer(retp, ninfo, info->u.ptr_info.own_info);
+ break;
+
+ case ARGTYPE_VOID:
+ case ARGTYPE_INT:
+ case ARGTYPE_UINT:
+ case ARGTYPE_LONG:
+ case ARGTYPE_ULONG:
+ case ARGTYPE_CHAR:
+ case ARGTYPE_SHORT:
+ case ARGTYPE_USHORT:
+ case ARGTYPE_FLOAT:
+ case ARGTYPE_DOUBLE:
+ *retp = *info;
+ break;
+ }
+
+ assert(!info->own_lens);
+ retp->lens = info->lens;
+ retp->own_lens = info->own_lens;
+ return 0;
+}
+
#ifdef ARCH_HAVE_SIZEOF
size_t arch_type_sizeof(struct process *proc, struct arg_type_info *arg);
#else
diff --git a/type.h b/type.h
index 3210677..0ffbd98 100644
--- a/type.h
+++ b/type.h
@@ -110,6 +110,10 @@
* itself. */
void type_destroy(struct arg_type_info *info);
+/* Copy type INFO into the area pointed to by RETP. Return 0 on
+ * success or a negative value on failure. */
+int type_clone(struct arg_type_info *retp, const struct arg_type_info *info);
+
/* Compute a size of given type. Return (size_t)-1 for error. */
size_t type_sizeof(struct process *proc, struct arg_type_info *type);
diff --git a/value.c b/value.c
index 8caf98c..2125ba9 100644
--- a/value.c
+++ b/value.c
@@ -189,15 +189,30 @@
value_clone(struct value *retp, const struct value *val)
{
*retp = *val;
+
+ if (val->own_type) {
+ retp->type = malloc(sizeof(struct arg_type_info));
+ if (type_clone (retp->type, val->type) < 0) {
+ free(retp->type);
+ return -1;
+ }
+ }
+
if (val->where == VAL_LOC_COPY) {
assert(val->inferior != NULL);
size_t size = type_sizeof(val->inferior, val->type);
- if (size == (size_t)-1)
+ if (size == (size_t)-1) {
+ fail:
+ if (retp->own_type) {
+ type_destroy(retp->type);
+ free(retp->type);
+ }
return -1;
+ }
retp->u.address = malloc(size);
if (retp->u.address == NULL)
- return -1;
+ goto fail;
memcpy(retp->u.address, val->u.address, size);
}