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