When TEMPORARY is in effect, proc_commit() destroys the temporary
dictionary. This means that any procedure that does not somehow disable
temporary transformations and refers to a variable following proc_commit()
has a use-after-free error.
T-TEST has two different bugs of this type. First, the loop that destroys
group statistics refers to destroyed variables. This commit fixes this
problem by instead using variable aux data destructors to destroy group
statistics.
Second, when there is an independent variable, destroying its values
requires knowing the variable's width. This commit fixes this problem by
destroying the values before calling proc_commit().
The AUTORECODE, DESCRIPTIVES, RANK, and REGRESSION procedures appear to
have similar issues (not fixed by this commit).
Reported by Jeremy Lavergne <jeremy@lavergne.gotdns.org>.
/* PSPP - a program for statistical analysis.
/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2009, 2010 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2009, 2010, 2011 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
static unsigned hash_group_binary (const struct group_statistics *g,
const struct t_test_proc *p);
static unsigned hash_group_binary (const struct group_statistics *g,
const struct t_test_proc *p);
+static void t_test_proc_destroy (struct t_test_proc *proc);
+
int
cmd_t_test (struct lexer *lexer, struct dataset *ds)
{
int
cmd_t_test (struct lexer *lexer, struct dataset *ds)
{
{
msg (SE, _("Exactly one of TESTVAL, GROUPS and PAIRS subcommands "
"must be specified."));
{
msg (SE, _("Exactly one of TESTVAL, GROUPS and PAIRS subcommands "
"must be specified."));
}
proc.mode = (cmd.sbc_testval ? T_1_SAMPLE
}
proc.mode = (cmd.sbc_testval ? T_1_SAMPLE
if (cmd.sbc_variables)
{
msg (SE, _("VARIABLES subcommand may not be used with PAIRS."));
if (cmd.sbc_variables)
{
msg (SE, _("VARIABLES subcommand may not be used with PAIRS."));
}
/* Fill proc.vars with the unique variables from pairs. */
}
/* Fill proc.vars with the unique variables from pairs. */
if (!cmd.n_variables)
{
msg (SE, _("One or more VARIABLES must be specified."));
if (!cmd.n_variables)
{
msg (SE, _("One or more VARIABLES must be specified."));
}
proc.n_vars = cmd.n_variables;
proc.vars = cmd.v_variables;
}
proc.n_vars = cmd.n_variables;
proc.vars = cmd.v_variables;
while (casegrouper_get_next_group (grouper, &group))
calculate (&proc, group, ds);
ok = casegrouper_destroy (grouper);
while (casegrouper_get_next_group (grouper, &group))
calculate (&proc, group, ds);
ok = casegrouper_destroy (grouper);
+
+ /* Free 'proc' then commit the procedure. Must happen in this order because
+ if proc->indep_var was created by a temporary transformation then
+ committing will destroy it. */
+ t_test_proc_destroy (&proc);
ok = proc_commit (ds) && ok;
ok = proc_commit (ds) && ok;
- if (proc.mode == T_IND_SAMPLES)
- {
- int v;
- /* Destroy any group statistics we created */
- for (v = 0; v < proc.n_vars; v++)
- {
- struct group_proc *grpp = group_proc_get (proc.vars[v]);
- hsh_destroy (grpp->group_hash);
- }
- }
+ return ok ? CMD_SUCCESS : CMD_FAILURE;
free_t_test (&cmd);
parse_failed:
free_t_test (&cmd);
parse_failed:
- if (proc.indep_var != NULL)
+ t_test_proc_destroy (&proc);
+ return CMD_FAILURE;
+}
+
+static void
+t_test_proc_destroy (struct t_test_proc *proc)
+{
+ if (proc->indep_var != NULL)
- int width = var_get_width (proc.indep_var);
- value_destroy (&proc.g_value[0], width);
- value_destroy (&proc.g_value[1], width);
+ int width = var_get_width (proc->indep_var);
+ value_destroy (&proc->g_value[0], width);
+ value_destroy (&proc->g_value[1], width);
- free (proc.vars);
- free (proc.pairs);
- return ok ? CMD_SUCCESS : CMD_FAILURE;
+ free (proc->vars);
+ free (proc->pairs);
+static void
+group_proc_dtor (struct variable *var)
+{
+ struct group_proc *group = var_detach_aux (var);
+
+ hsh_destroy (group->group_hash);
+ free (group);
+}
struct group_proc *
group_proc_get (const struct variable *v)
struct group_proc *
group_proc_get (const struct variable *v)
struct group_proc *group = var_get_aux (v);
if (group == NULL)
{
struct group_proc *group = var_get_aux (v);
if (group == NULL)
{
- group = xmalloc (sizeof (struct group_proc));
- var_attach_aux (v, group, var_dtor_free);
+ group = xzalloc (sizeof *group);
+ var_attach_aux (v, group, group_proc_dtor);