script: Avoid a use-after-free when redefining a function during execution

Defining a new function with the same name as a previously defined
function causes the grub_script and associated resources for the
previous function to be freed. If the previous function is currently
executing when a function with the same name is defined, this results
in use-after-frees when processing subsequent commands in the original
function.

Instead, reject a new function definition if it has the same name as
a previously defined function, and that function is currently being
executed. Although a behavioural change, this should be backwards
compatible with existing configurations because they can't be
dependent on the current behaviour without being broken.

Fixes: CVE-2020-15706

Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
Chris Coulson 2020-07-10 14:41:45 +01:00 committed by Daniel Kiper
parent 1a8d9c9b4a
commit 426f57383d
4 changed files with 19 additions and 4 deletions

View file

@ -838,7 +838,9 @@ grub_script_function_call (grub_script_function_t func, int argc, char **args)
old_scope = scope; old_scope = scope;
scope = &new_scope; scope = &new_scope;
func->executing++;
ret = grub_script_execute (func->func); ret = grub_script_execute (func->func);
func->executing--;
function_return = 0; function_return = 0;
active_loops = loops; active_loops = loops;

View file

@ -34,6 +34,7 @@ grub_script_function_create (struct grub_script_arg *functionname_arg,
func = (grub_script_function_t) grub_malloc (sizeof (*func)); func = (grub_script_function_t) grub_malloc (sizeof (*func));
if (! func) if (! func)
return 0; return 0;
func->executing = 0;
func->name = grub_strdup (functionname_arg->str); func->name = grub_strdup (functionname_arg->str);
if (! func->name) if (! func->name)
@ -60,11 +61,20 @@ grub_script_function_create (struct grub_script_arg *functionname_arg,
grub_script_function_t q; grub_script_function_t q;
q = *p; q = *p;
grub_free (func);
if (q->executing > 0)
{
grub_error (GRUB_ERR_BAD_ARGUMENT,
N_("attempt to redefine a function being executed"));
func = NULL;
}
else
{
grub_script_free (q->func); grub_script_free (q->func);
q->func = cmd; q->func = cmd;
grub_free (func);
func = q; func = q;
} }
}
else else
{ {
func->next = *p; func->next = *p;

View file

@ -289,7 +289,8 @@ function: "function" "name"
grub_script_mem_free (state->func_mem); grub_script_mem_free (state->func_mem);
else { else {
script->children = state->scripts; script->children = state->scripts;
grub_script_function_create ($2, script); if (!grub_script_function_create ($2, script))
grub_script_free (script);
} }
state->scripts = $<scripts>3; state->scripts = $<scripts>3;

View file

@ -361,6 +361,8 @@ struct grub_script_function
/* The next element. */ /* The next element. */
struct grub_script_function *next; struct grub_script_function *next;
unsigned executing;
}; };
typedef struct grub_script_function *grub_script_function_t; typedef struct grub_script_function *grub_script_function_t;