mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-24 08:09:22 +00:00
kern/dl: Fix for an integer overflow in grub_dl_ref()
It was possible to overflow the value of mod->ref_count, a signed integer, by repeatedly invoking insmod on an already loaded module. This led to a use-after-free. As once ref_count was overflowed it became possible to unload the module while there was still references to it. This resolves the issue by using grub_add() to check if the ref_count will overflow and then stops further increments. Further changes were also made to grub_dl_unref() to check for the underflow condition and the reference count was changed to an unsigned 64-bit integer. Reported-by: B Horn <b@horn.uk> Signed-off-by: B Horn <b@horn.uk> Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
1 parent
2c34af908e
commit
500e5fdd82
4 files changed
+19
-12
No files matched your search
@@ -167,7 +167,7 @@ grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)),
|
|||||||
{
|
{
|
||||||
grub_dl_dep_t dep;
|
grub_dl_dep_t dep;
|
||||||
|
|
||||||
grub_printf ("%s\t%d\t\t", mod->name, mod->ref_count);
|
grub_printf ("%s\t%" PRIuGRUB_UINT64_T "\t\t", mod->name, mod->ref_count);
|
||||||
for (dep = mod->dep; dep; dep = dep->next)
|
for (dep = mod->dep; dep; dep = dep->next)
|
||||||
{
|
{
|
||||||
if (dep != mod->dep)
|
if (dep != mod->dep)
|
||||||
|
|||||||
+12
-5
@@ -32,6 +32,7 @@
|
|||||||
#include <grub/env.h>
|
#include <grub/env.h>
|
||||||
#include <grub/cache.h>
|
#include <grub/cache.h>
|
||||||
#include <grub/i18n.h>
|
#include <grub/i18n.h>
|
||||||
|
#include <grub/safemath.h>
|
||||||
|
|
||||||
#ifdef GRUB_MACHINE_EFI
|
#ifdef GRUB_MACHINE_EFI
|
||||||
#include <grub/efi/memory.h>
|
#include <grub/efi/memory.h>
|
||||||
@@ -556,7 +557,7 @@ grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
|
|||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
grub_uint64_t
|
||||||
grub_dl_ref (grub_dl_t mod)
|
grub_dl_ref (grub_dl_t mod)
|
||||||
{
|
{
|
||||||
grub_dl_dep_t dep;
|
grub_dl_dep_t dep;
|
||||||
@@ -567,10 +568,13 @@ grub_dl_ref (grub_dl_t mod)
|
|||||||
for (dep = mod->dep; dep; dep = dep->next)
|
for (dep = mod->dep; dep; dep = dep->next)
|
||||||
grub_dl_ref (dep->mod);
|
grub_dl_ref (dep->mod);
|
||||||
|
|
||||||
return ++mod->ref_count;
|
if (grub_add (mod->ref_count, 1, &mod->ref_count))
|
||||||
|
grub_fatal ("Module reference count overflow");
|
||||||
|
|
||||||
|
return mod->ref_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
grub_uint64_t
|
||||||
grub_dl_unref (grub_dl_t mod)
|
grub_dl_unref (grub_dl_t mod)
|
||||||
{
|
{
|
||||||
grub_dl_dep_t dep;
|
grub_dl_dep_t dep;
|
||||||
@@ -581,10 +585,13 @@ grub_dl_unref (grub_dl_t mod)
|
|||||||
for (dep = mod->dep; dep; dep = dep->next)
|
for (dep = mod->dep; dep; dep = dep->next)
|
||||||
grub_dl_unref (dep->mod);
|
grub_dl_unref (dep->mod);
|
||||||
|
|
||||||
return --mod->ref_count;
|
if (grub_sub (mod->ref_count, 1, &mod->ref_count))
|
||||||
|
grub_fatal ("Module reference count underflow");
|
||||||
|
|
||||||
|
return mod->ref_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
grub_uint64_t
|
||||||
grub_dl_ref_count (grub_dl_t mod)
|
grub_dl_ref_count (grub_dl_t mod)
|
||||||
{
|
{
|
||||||
if (mod == NULL)
|
if (mod == NULL)
|
||||||
|
|||||||
+4
-4
@@ -174,7 +174,7 @@ typedef struct grub_dl_dep *grub_dl_dep_t;
|
|||||||
struct grub_dl
|
struct grub_dl
|
||||||
{
|
{
|
||||||
char *name;
|
char *name;
|
||||||
int ref_count;
|
grub_uint64_t ref_count;
|
||||||
int persistent;
|
int persistent;
|
||||||
grub_dl_dep_t dep;
|
grub_dl_dep_t dep;
|
||||||
grub_dl_segment_t segment;
|
grub_dl_segment_t segment;
|
||||||
@@ -203,9 +203,9 @@ grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name);
|
|||||||
grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
|
grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
|
||||||
grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size);
|
grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size);
|
||||||
int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod);
|
int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod);
|
||||||
extern int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
|
extern grub_uint64_t EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
|
||||||
extern int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
|
extern grub_uint64_t EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
|
||||||
extern int EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod);
|
extern grub_uint64_t EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod);
|
||||||
|
|
||||||
extern grub_dl_t EXPORT_VAR(grub_dl_head);
|
extern grub_dl_t EXPORT_VAR(grub_dl_head);
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -190,14 +190,14 @@ grub_xputs_real (const char *str)
|
|||||||
|
|
||||||
void (*grub_xputs) (const char *str) = grub_xputs_real;
|
void (*grub_xputs) (const char *str) = grub_xputs_real;
|
||||||
|
|
||||||
int
|
grub_uint64_t
|
||||||
grub_dl_ref (grub_dl_t mod)
|
grub_dl_ref (grub_dl_t mod)
|
||||||
{
|
{
|
||||||
(void) mod;
|
(void) mod;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
grub_uint64_t
|
||||||
grub_dl_unref (grub_dl_t mod)
|
grub_dl_unref (grub_dl_t mod)
|
||||||
{
|
{
|
||||||
(void) mod;
|
(void) mod;
|
||||||
|
|||||||
Reference in new issue
Block a user