grub/util/resolve.c
Glenn Washburn ddf47bdb04 util/resolve: Do not read past the end of the array in read_dep_list()
If the last non-NULL byte of "buf" is not a white-space character (such as
when a read line is longer than the size of "buf"), then "p" will eventually
point to the byte after the last byte in "buf". After which "p" will be
dereferenced in the while conditional leading to an out of bounds read. Make
sure that "p" is inside "buf" before dereferencing it.

Signed-off-by: Glenn Washburn <development@efficientek.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2022-02-08 13:39:01 +01:00

288 lines
5.7 KiB
C

/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2002,2007 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <grub/emu/misc.h>
#include <grub/misc.h>
#include <grub/util/misc.h>
#include <grub/util/resolve.h>
#include <grub/i18n.h>
/* Module. */
struct mod_list
{
const char *name;
struct mod_list *next;
};
/* Dependency. */
struct dep_list
{
const char *name;
struct mod_list *list;
struct dep_list *next;
};
static char buf[1024];
static void
free_mod_list (struct mod_list *head)
{
while (head)
{
struct mod_list *next;
next = head->next;
free ((void *) head->name);
free (head);
head = next;
}
}
static void
free_dep_list (struct dep_list *head)
{
while (head)
{
struct dep_list *next;
next = head->next;
free ((void *) head->name);
free_mod_list (head->list);
free (head);
head = next;
}
}
/* Read the list of dependencies. */
static struct dep_list *
read_dep_list (FILE *fp)
{
struct dep_list *dep_list = 0;
while (fgets (buf, sizeof (buf), fp))
{
char *p;
struct dep_list *dep;
/* Get the target name. */
p = strchr (buf, ':');
if (! p)
grub_util_error (_("invalid line format: %s"), buf);
*p++ = '\0';
dep = xmalloc (sizeof (*dep));
dep->name = xstrdup (buf);
dep->list = 0;
dep->next = dep_list;
dep_list = dep;
/* Add dependencies. */
while (p < (buf + sizeof (buf)) && *p)
{
struct mod_list *mod;
char *name;
/* Skip whitespace. */
while (*p && grub_isspace (*p))
p++;
if (! *p)
break;
name = p;
/* Skip non-whitespace. */
while (*p && ! grub_isspace (*p))
p++;
*p++ = '\0';
mod = (struct mod_list *) xmalloc (sizeof (*mod));
mod->name = xstrdup (name);
mod->next = dep->list;
dep->list = mod;
}
}
return dep_list;
}
static char *
get_module_name (const char *str)
{
char *base;
char *ext;
base = strrchr (str, '/');
if (! base)
base = (char *) str;
else
base++;
ext = strrchr (base, '.');
if (ext && strcmp (ext, ".mod") == 0)
{
char *name;
name = xmalloc (ext - base + 1);
memcpy (name, base, ext - base);
name[ext - base] = '\0';
return name;
}
return xstrdup (base);
}
static char *
get_module_path (const char *prefix, const char *str)
{
char *dir;
char *base;
char *ext;
char *ret;
ext = strrchr (str, '.');
if (ext && strcmp (ext, ".mod") == 0)
base = xstrdup (str);
else
{
base = xmalloc (strlen (str) + 4 + 1);
sprintf (base, "%s.mod", str);
}
dir = strchr (str, '/');
if (dir)
return base;
ret = grub_util_get_path (prefix, base);
free (base);
return ret;
}
static void
add_module (const char *dir,
struct dep_list *dep_list,
struct mod_list **mod_head,
struct grub_util_path_list **path_head,
const char *name)
{
char *mod_name;
struct grub_util_path_list *path;
struct mod_list *mod;
struct dep_list *dep;
mod_name = get_module_name (name);
/* Check if the module has already been added. */
for (mod = *mod_head; mod; mod = mod->next)
if (strcmp (mod->name, mod_name) == 0)
{
free (mod_name);
return;
}
/* Resolve dependencies. */
for (dep = dep_list; dep; dep = dep->next)
if (strcmp (dep->name, mod_name) == 0)
{
for (mod = dep->list; mod; mod = mod->next)
add_module (dir, dep_list, mod_head, path_head, mod->name);
break;
}
/* Add this module. */
mod = (struct mod_list *) xmalloc (sizeof (*mod));
mod->name = mod_name;
mod->next = *mod_head;
*mod_head = mod;
/* Add this path. */
path = (struct grub_util_path_list *) xmalloc (sizeof (*path));
path->name = get_module_path (dir, name);
path->next = *path_head;
*path_head = path;
}
struct grub_util_path_list *
grub_util_resolve_dependencies (const char *prefix,
const char *dep_list_file,
char *modules[])
{
char *path;
FILE *fp;
struct dep_list *dep_list;
struct mod_list *mod_list = 0;
struct grub_util_path_list *path_list = 0;
path = grub_util_get_path (prefix, dep_list_file);
fp = grub_util_fopen (path, "r");
if (! fp)
grub_util_error (_("cannot open `%s': %s"), path, strerror (errno));
free (path);
dep_list = read_dep_list (fp);
fclose (fp);
while (*modules)
{
add_module (prefix, dep_list, &mod_list, &path_list, *modules);
modules++;
}
free_dep_list (dep_list);
free_mod_list (mod_list);
{ /* Reverse the path_list */
struct grub_util_path_list *p, *prev, *next;
for (p = path_list, prev = NULL; p; p = next)
{
next = p->next;
p->next = prev;
prev = p;
}
return prev;
}
}
void
grub_util_free_path_list (struct grub_util_path_list *path_list)
{
struct grub_util_path_list *next;
while (path_list)
{
next = path_list->next;
free ((void *) path_list->name);
free (path_list);
path_list = next;
}
}