0029-kern-dl-Fix-for-an-integer-overflow-in-grub_dl_ref.patch 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. From 4d70ddc5255b6d3f752da4120f593d7007222ca2 Mon Sep 17 00:00:00 2001
  2. From: B Horn <b@horn.uk>
  3. Date: Thu, 18 Apr 2024 15:59:26 +0100
  4. Subject: [PATCH] kern/dl: Fix for an integer overflow in grub_dl_ref()
  5. It was possible to overflow the value of mod->ref_count, a signed
  6. integer, by repeatedly invoking insmod on an already loaded module.
  7. This led to a use-after-free. As once ref_count was overflowed it became
  8. possible to unload the module while there was still references to it.
  9. This resolves the issue by using grub_add() to check if the ref_count
  10. will overflow and then stops further increments. Further changes were
  11. also made to grub_dl_unref() to check for the underflow condition and
  12. the reference count was changed to an unsigned 64-bit integer.
  13. Reported-by: B Horn <b@horn.uk>
  14. Signed-off-by: B Horn <b@horn.uk>
  15. Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
  16. Upstream: 500e5fdd82ca40412b0b73f5e5dda38e4a3af96d
  17. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
  18. ---
  19. grub-core/commands/minicmd.c | 2 +-
  20. grub-core/kern/dl.c | 17 ++++++++++++-----
  21. include/grub/dl.h | 8 ++++----
  22. util/misc.c | 4 ++--
  23. 4 files changed, 19 insertions(+), 12 deletions(-)
  24. diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c
  25. index fa498931e..286290866 100644
  26. --- a/grub-core/commands/minicmd.c
  27. +++ b/grub-core/commands/minicmd.c
  28. @@ -167,7 +167,7 @@ grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)),
  29. {
  30. grub_dl_dep_t dep;
  31. - grub_printf ("%s\t%d\t\t", mod->name, mod->ref_count);
  32. + grub_printf ("%s\t%" PRIuGRUB_UINT64_T "\t\t", mod->name, mod->ref_count);
  33. for (dep = mod->dep; dep; dep = dep->next)
  34. {
  35. if (dep != mod->dep)
  36. diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
  37. index 0bf40caa6..1a38742e6 100644
  38. --- a/grub-core/kern/dl.c
  39. +++ b/grub-core/kern/dl.c
  40. @@ -32,6 +32,7 @@
  41. #include <grub/env.h>
  42. #include <grub/cache.h>
  43. #include <grub/i18n.h>
  44. +#include <grub/safemath.h>
  45. /* Platforms where modules are in a readonly area of memory. */
  46. #if defined(GRUB_MACHINE_QEMU)
  47. @@ -532,7 +533,7 @@ grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
  48. return GRUB_ERR_NONE;
  49. }
  50. -int
  51. +grub_uint64_t
  52. grub_dl_ref (grub_dl_t mod)
  53. {
  54. grub_dl_dep_t dep;
  55. @@ -543,10 +544,13 @@ grub_dl_ref (grub_dl_t mod)
  56. for (dep = mod->dep; dep; dep = dep->next)
  57. grub_dl_ref (dep->mod);
  58. - return ++mod->ref_count;
  59. + if (grub_add (mod->ref_count, 1, &mod->ref_count))
  60. + grub_fatal ("Module reference count overflow");
  61. +
  62. + return mod->ref_count;
  63. }
  64. -int
  65. +grub_uint64_t
  66. grub_dl_unref (grub_dl_t mod)
  67. {
  68. grub_dl_dep_t dep;
  69. @@ -557,10 +561,13 @@ grub_dl_unref (grub_dl_t mod)
  70. for (dep = mod->dep; dep; dep = dep->next)
  71. grub_dl_unref (dep->mod);
  72. - return --mod->ref_count;
  73. + if (grub_sub (mod->ref_count, 1, &mod->ref_count))
  74. + grub_fatal ("Module reference count underflow");
  75. +
  76. + return mod->ref_count;
  77. }
  78. -int
  79. +grub_uint64_t
  80. grub_dl_ref_count (grub_dl_t mod)
  81. {
  82. if (mod == NULL)
  83. diff --git a/include/grub/dl.h b/include/grub/dl.h
  84. index cd1f46c8b..f0a94e273 100644
  85. --- a/include/grub/dl.h
  86. +++ b/include/grub/dl.h
  87. @@ -174,7 +174,7 @@ typedef struct grub_dl_dep *grub_dl_dep_t;
  88. struct grub_dl
  89. {
  90. char *name;
  91. - int ref_count;
  92. + grub_uint64_t ref_count;
  93. int persistent;
  94. grub_dl_dep_t dep;
  95. grub_dl_segment_t segment;
  96. @@ -203,9 +203,9 @@ grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name);
  97. grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
  98. grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size);
  99. int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod);
  100. -extern int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
  101. -extern int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
  102. -extern int EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod);
  103. +extern grub_uint64_t EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
  104. +extern grub_uint64_t EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
  105. +extern grub_uint64_t EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod);
  106. extern grub_dl_t EXPORT_VAR(grub_dl_head);
  107. diff --git a/util/misc.c b/util/misc.c
  108. index d545212d9..0f928e5b4 100644
  109. --- a/util/misc.c
  110. +++ b/util/misc.c
  111. @@ -190,14 +190,14 @@ grub_xputs_real (const char *str)
  112. void (*grub_xputs) (const char *str) = grub_xputs_real;
  113. -int
  114. +grub_uint64_t
  115. grub_dl_ref (grub_dl_t mod)
  116. {
  117. (void) mod;
  118. return 0;
  119. }
  120. -int
  121. +grub_uint64_t
  122. grub_dl_unref (grub_dl_t mod)
  123. {
  124. (void) mod;
  125. --
  126. 2.50.1