Skip to content

Commit 500e5fd

Browse files
bahornDaniel Kiper
authored and
Daniel Kiper
committed
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 <[email protected]> Signed-off-by: B Horn <[email protected]> Reviewed-by: Daniel Kiper <[email protected]>
1 parent 2c34af9 commit 500e5fd

File tree

4 files changed

+19
-12
lines changed

4 files changed

+19
-12
lines changed

grub-core/commands/minicmd.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)),
167167
{
168168
grub_dl_dep_t dep;
169169

170-
grub_printf ("%s\t%d\t\t", mod->name, mod->ref_count);
170+
grub_printf ("%s\t%" PRIuGRUB_UINT64_T "\t\t", mod->name, mod->ref_count);
171171
for (dep = mod->dep; dep; dep = dep->next)
172172
{
173173
if (dep != mod->dep)

grub-core/kern/dl.c

+12-5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <grub/env.h>
3333
#include <grub/cache.h>
3434
#include <grub/i18n.h>
35+
#include <grub/safemath.h>
3536

3637
#ifdef GRUB_MACHINE_EFI
3738
#include <grub/efi/memory.h>
@@ -556,7 +557,7 @@ grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
556557
return GRUB_ERR_NONE;
557558
}
558559

559-
int
560+
grub_uint64_t
560561
grub_dl_ref (grub_dl_t mod)
561562
{
562563
grub_dl_dep_t dep;
@@ -567,10 +568,13 @@ grub_dl_ref (grub_dl_t mod)
567568
for (dep = mod->dep; dep; dep = dep->next)
568569
grub_dl_ref (dep->mod);
569570

570-
return ++mod->ref_count;
571+
if (grub_add (mod->ref_count, 1, &mod->ref_count))
572+
grub_fatal ("Module reference count overflow");
573+
574+
return mod->ref_count;
571575
}
572576

573-
int
577+
grub_uint64_t
574578
grub_dl_unref (grub_dl_t mod)
575579
{
576580
grub_dl_dep_t dep;
@@ -581,10 +585,13 @@ grub_dl_unref (grub_dl_t mod)
581585
for (dep = mod->dep; dep; dep = dep->next)
582586
grub_dl_unref (dep->mod);
583587

584-
return --mod->ref_count;
588+
if (grub_sub (mod->ref_count, 1, &mod->ref_count))
589+
grub_fatal ("Module reference count underflow");
590+
591+
return mod->ref_count;
585592
}
586593

587-
int
594+
grub_uint64_t
588595
grub_dl_ref_count (grub_dl_t mod)
589596
{
590597
if (mod == NULL)

include/grub/dl.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ typedef struct grub_dl_dep *grub_dl_dep_t;
174174
struct grub_dl
175175
{
176176
char *name;
177-
int ref_count;
177+
grub_uint64_t ref_count;
178178
int persistent;
179179
grub_dl_dep_t dep;
180180
grub_dl_segment_t segment;
@@ -203,9 +203,9 @@ grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name);
203203
grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
204204
grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size);
205205
int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod);
206-
extern int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
207-
extern int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
208-
extern int EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod);
206+
extern grub_uint64_t EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
207+
extern grub_uint64_t EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
208+
extern grub_uint64_t EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod);
209209

210210
extern grub_dl_t EXPORT_VAR(grub_dl_head);
211211

util/misc.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,14 @@ grub_xputs_real (const char *str)
190190

191191
void (*grub_xputs) (const char *str) = grub_xputs_real;
192192

193-
int
193+
grub_uint64_t
194194
grub_dl_ref (grub_dl_t mod)
195195
{
196196
(void) mod;
197197
return 0;
198198
}
199199

200-
int
200+
grub_uint64_t
201201
grub_dl_unref (grub_dl_t mod)
202202
{
203203
(void) mod;

0 commit comments

Comments
 (0)