Blame SOURCES/0348-dl-Only-allow-unloading-modules-that-are-not-depende.patch

b1bcb2
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
b1bcb2
From: Javier Martinez Canillas <javierm@redhat.com>
b1bcb2
Date: Tue, 29 Sep 2020 14:08:55 +0200
b1bcb2
Subject: [PATCH] dl: Only allow unloading modules that are not dependencies
b1bcb2
b1bcb2
When a module is attempted to be removed its reference counter is always
b1bcb2
decremented. This means that repeated rmmod invocations will cause the
b1bcb2
module to be unloaded even if another module depends on it.
b1bcb2
b1bcb2
This may lead to a use-after-free scenario allowing an attacker to execute
b1bcb2
arbitrary code and by-pass the UEFI Secure Boot protection.
b1bcb2
b1bcb2
While being there, add the extern keyword to some function declarations in
b1bcb2
that header file.
b1bcb2
b1bcb2
Fixes: CVE-2020-25632
b1bcb2
b1bcb2
Reported-by: Chris Coulson <chris.coulson@canonical.com>
b1bcb2
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
b1bcb2
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
b1bcb2
---
b1bcb2
 grub-core/commands/minicmd.c | 7 +++++--
b1bcb2
 grub-core/kern/dl.c          | 9 +++++++++
b1bcb2
 include/grub/dl.h            | 8 +++++---
b1bcb2
 3 files changed, 19 insertions(+), 5 deletions(-)
b1bcb2
b1bcb2
diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c
b1bcb2
index b25ca4b9f17..4660a020bda 100644
b1bcb2
--- a/grub-core/commands/minicmd.c
b1bcb2
+++ b/grub-core/commands/minicmd.c
b1bcb2
@@ -137,8 +137,11 @@ grub_mini_cmd_rmmod (struct grub_command *cmd __attribute__ ((unused)),
b1bcb2
   if (! mod)
b1bcb2
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such module");
b1bcb2
 
b1bcb2
-  if (grub_dl_unref (mod) <= 0)
b1bcb2
-    grub_dl_unload (mod);
b1bcb2
+  if (grub_dl_ref_count (mod) > 1)
b1bcb2
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "cannot unload referenced module");
b1bcb2
+
b1bcb2
+  grub_dl_unref (mod);
b1bcb2
+  grub_dl_unload (mod);
b1bcb2
 
b1bcb2
   return 0;
b1bcb2
 }
b1bcb2
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
b1bcb2
index c45afc64950..fb88894aba5 100644
b1bcb2
--- a/grub-core/kern/dl.c
b1bcb2
+++ b/grub-core/kern/dl.c
b1bcb2
@@ -557,6 +557,15 @@ grub_dl_unref (grub_dl_t mod)
b1bcb2
   return --mod->ref_count;
b1bcb2
 }
b1bcb2
 
b1bcb2
+int
b1bcb2
+grub_dl_ref_count (grub_dl_t mod)
b1bcb2
+{
b1bcb2
+  if (mod == NULL)
b1bcb2
+    return 0;
b1bcb2
+
b1bcb2
+  return mod->ref_count;
b1bcb2
+}
b1bcb2
+
b1bcb2
 static void
b1bcb2
 grub_dl_flush_cache (grub_dl_t mod)
b1bcb2
 {
b1bcb2
diff --git a/include/grub/dl.h b/include/grub/dl.h
b1bcb2
index 9562fa6634c..c0e2c55c8c7 100644
b1bcb2
--- a/include/grub/dl.h
b1bcb2
+++ b/include/grub/dl.h
b1bcb2
@@ -202,9 +202,11 @@ grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name);
b1bcb2
 grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
b1bcb2
 grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size);
b1bcb2
 int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod);
b1bcb2
-void grub_dl_unload_unneeded (void);
b1bcb2
-int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
b1bcb2
-int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
b1bcb2
+extern void grub_dl_unload_unneeded (void);
b1bcb2
+extern int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
b1bcb2
+extern int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
b1bcb2
+extern int EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod);
b1bcb2
+
b1bcb2
 extern grub_dl_t EXPORT_VAR(grub_dl_head);
b1bcb2
 
b1bcb2
 #ifndef GRUB_UTIL