0132-kern-parser-Fix-a-stack-buffer-overflow.patch 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. From 4ea7bae51f97e49c84dc67ea30b466ca8633b9f6 Mon Sep 17 00:00:00 2001
  2. From: Chris Coulson <chris.coulson@canonical.com>
  3. Date: Thu, 7 Jan 2021 19:21:03 +0000
  4. Subject: [PATCH] kern/parser: Fix a stack buffer overflow
  5. grub_parser_split_cmdline() expands variable names present in the supplied
  6. command line in to their corresponding variable contents and uses a 1 kiB
  7. stack buffer for temporary storage without sufficient bounds checking. If
  8. the function is called with a command line that references a variable with
  9. a sufficiently large payload, it is possible to overflow the stack
  10. buffer via tab completion, corrupt the stack frame and potentially
  11. control execution.
  12. Fixes: CVE-2020-27749
  13. Reported-by: Chris Coulson <chris.coulson@canonical.com>
  14. Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
  15. Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
  16. Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
  17. Signed-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
  18. ---
  19. grub-core/kern/parser.c | 110 +++++++++++++++++++++++++++++-------------------
  20. 1 file changed, 67 insertions(+), 43 deletions(-)
  21. diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c
  22. index e010eaa..6ab7aa4 100644
  23. --- a/grub-core/kern/parser.c
  24. +++ b/grub-core/kern/parser.c
  25. @@ -18,6 +18,7 @@
  26. */
  27. #include <grub/parser.h>
  28. +#include <grub/buffer.h>
  29. #include <grub/env.h>
  30. #include <grub/misc.h>
  31. #include <grub/mm.h>
  32. @@ -107,8 +108,8 @@ check_varstate (grub_parser_state_t s)
  33. }
  34. -static void
  35. -add_var (char *varname, char **bp, char **vp,
  36. +static grub_err_t
  37. +add_var (grub_buffer_t varname, grub_buffer_t buf,
  38. grub_parser_state_t state, grub_parser_state_t newstate)
  39. {
  40. const char *val;
  41. @@ -116,31 +117,41 @@ add_var (char *varname, char **bp, char **vp,
  42. /* Check if a variable was being read in and the end of the name
  43. was reached. */
  44. if (!(check_varstate (state) && !check_varstate (newstate)))
  45. - return;
  46. + return GRUB_ERR_NONE;
  47. +
  48. + if (grub_buffer_append_char (varname, '\0') != GRUB_ERR_NONE)
  49. + return grub_errno;
  50. - *((*vp)++) = '\0';
  51. - val = grub_env_get (varname);
  52. - *vp = varname;
  53. + val = grub_env_get ((const char *) grub_buffer_peek_data (varname));
  54. + grub_buffer_reset (varname);
  55. if (!val)
  56. - return;
  57. + return GRUB_ERR_NONE;
  58. /* Insert the contents of the variable in the buffer. */
  59. - for (; *val; val++)
  60. - *((*bp)++) = *val;
  61. + return grub_buffer_append_data (buf, val, grub_strlen (val));
  62. }
  63. -static void
  64. -terminate_arg (char *buffer, char **bp, int *argc)
  65. +static grub_err_t
  66. +terminate_arg (grub_buffer_t buffer, int *argc)
  67. {
  68. - if (*bp != buffer && *((*bp) - 1) != '\0')
  69. - {
  70. - *((*bp)++) = '\0';
  71. - (*argc)++;
  72. - }
  73. + grub_size_t unread = grub_buffer_get_unread_bytes (buffer);
  74. +
  75. + if (unread == 0)
  76. + return GRUB_ERR_NONE;
  77. +
  78. + if (*(const char *) grub_buffer_peek_data_at (buffer, unread - 1) == '\0')
  79. + return GRUB_ERR_NONE;
  80. +
  81. + if (grub_buffer_append_char (buffer, '\0') != GRUB_ERR_NONE)
  82. + return grub_errno;
  83. +
  84. + (*argc)++;
  85. +
  86. + return GRUB_ERR_NONE;
  87. }
  88. static grub_err_t
  89. -process_char (char c, char *buffer, char **bp, char *varname, char **vp,
  90. +process_char (char c, grub_buffer_t buffer, grub_buffer_t varname,
  91. grub_parser_state_t state, int *argc,
  92. grub_parser_state_t *newstate)
  93. {
  94. @@ -153,12 +164,13 @@ process_char (char c, char *buffer, char **bp, char *varname, char **vp,
  95. * not describe the variable anymore, write the variable to
  96. * the buffer.
  97. */
  98. - add_var (varname, bp, vp, state, *newstate);
  99. + if (add_var (varname, buffer, state, *newstate) != GRUB_ERR_NONE)
  100. + return grub_errno;
  101. if (check_varstate (*newstate))
  102. {
  103. if (use)
  104. - *((*vp)++) = use;
  105. + return grub_buffer_append_char (varname, use);
  106. }
  107. else if (*newstate == GRUB_PARSER_STATE_TEXT &&
  108. state != GRUB_PARSER_STATE_ESC && grub_isspace (use))
  109. @@ -167,10 +179,10 @@ process_char (char c, char *buffer, char **bp, char *varname, char **vp,
  110. * Don't add more than one argument if multiple
  111. * spaces are used.
  112. */
  113. - terminate_arg (buffer, bp, argc);
  114. + return terminate_arg (buffer, argc);
  115. }
  116. else if (use)
  117. - *((*bp)++) = use;
  118. + return grub_buffer_append_char (buffer, use);
  119. return GRUB_ERR_NONE;
  120. }
  121. @@ -181,19 +193,22 @@ grub_parser_split_cmdline (const char *cmdline,
  122. int *argc, char ***argv)
  123. {
  124. grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
  125. - /* XXX: Fixed size buffer, perhaps this buffer should be dynamically
  126. - allocated. */
  127. - char buffer[1024];
  128. - char *bp = buffer;
  129. + grub_buffer_t buffer, varname;
  130. char *rd = (char *) cmdline;
  131. char *rp = rd;
  132. - char varname[200];
  133. - char *vp = varname;
  134. - char *args;
  135. int i;
  136. *argc = 0;
  137. *argv = NULL;
  138. +
  139. + buffer = grub_buffer_new (1024);
  140. + if (buffer == NULL)
  141. + return grub_errno;
  142. +
  143. + varname = grub_buffer_new (200);
  144. + if (varname == NULL)
  145. + goto fail;
  146. +
  147. do
  148. {
  149. if (rp == NULL || *rp == '\0')
  150. @@ -219,7 +234,7 @@ grub_parser_split_cmdline (const char *cmdline,
  151. {
  152. grub_parser_state_t newstate;
  153. - if (process_char (*rp, buffer, &bp, varname, &vp, state, argc,
  154. + if (process_char (*rp, buffer, varname, state, argc,
  155. &newstate) != GRUB_ERR_NONE)
  156. goto fail;
  157. @@ -230,10 +245,12 @@ grub_parser_split_cmdline (const char *cmdline,
  158. /* A special case for when the last character was part of a
  159. variable. */
  160. - add_var (varname, &bp, &vp, state, GRUB_PARSER_STATE_TEXT);
  161. + if (add_var (varname, buffer, state, GRUB_PARSER_STATE_TEXT) != GRUB_ERR_NONE)
  162. + goto fail;
  163. /* Ensure that the last argument is terminated. */
  164. - terminate_arg (buffer, &bp, argc);
  165. + if (terminate_arg (buffer, argc) != GRUB_ERR_NONE)
  166. + goto fail;
  167. /* If there are no args, then we're done. */
  168. if (!*argc)
  169. @@ -242,38 +259,45 @@ grub_parser_split_cmdline (const char *cmdline,
  170. goto out;
  171. }
  172. - /* Reserve memory for the return values. */
  173. - args = grub_malloc (bp - buffer);
  174. - if (!args)
  175. - goto fail;
  176. - grub_memcpy (args, buffer, bp - buffer);
  177. -
  178. *argv = grub_calloc (*argc + 1, sizeof (char *));
  179. if (!*argv)
  180. goto fail;
  181. /* The arguments are separated with 0's, setup argv so it points to
  182. the right values. */
  183. - bp = args;
  184. for (i = 0; i < *argc; i++)
  185. {
  186. - (*argv)[i] = bp;
  187. - while (*bp)
  188. - bp++;
  189. - bp++;
  190. + char *arg;
  191. +
  192. + if (i > 0)
  193. + {
  194. + if (grub_buffer_advance_read_pos (buffer, 1) != GRUB_ERR_NONE)
  195. + goto fail;
  196. + }
  197. +
  198. + arg = (char *) grub_buffer_peek_data (buffer);
  199. + if (arg == NULL ||
  200. + grub_buffer_advance_read_pos (buffer, grub_strlen (arg)) != GRUB_ERR_NONE)
  201. + goto fail;
  202. +
  203. + (*argv)[i] = arg;
  204. }
  205. + /* Keep memory for the return values. */
  206. + grub_buffer_take_data (buffer);
  207. +
  208. grub_errno = GRUB_ERR_NONE;
  209. out:
  210. if (rd != cmdline)
  211. grub_free (rd);
  212. + grub_buffer_free (buffer);
  213. + grub_buffer_free (varname);
  214. return grub_errno;
  215. fail:
  216. grub_free (*argv);
  217. - grub_free (args);
  218. goto out;
  219. }
  220. --
  221. 2.14.2