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

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