From: Lidong Chen <lidong.c...@oracle.com>

The test_parse() evaluates test expression recursively. Due to lack of
recursion depth check a specially crafted expression may cause a stack
overflow. The recursion is only triggered by the parentheses usage and
it can be unlimited. However, sensible expressions are unlikely to
contain more than a few parentheses. So, this patch limits the recursion
depth to 100, which should be sufficient.

Reported-by: Nils Langius <n...@langius.de>
Signed-off-by: Lidong Chen <lidong.c...@oracle.com>
Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com>
---
 grub-core/commands/test.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/grub-core/commands/test.c b/grub-core/commands/test.c
index 62d3fb398..b585c3d70 100644
--- a/grub-core/commands/test.c
+++ b/grub-core/commands/test.c
@@ -29,6 +29,9 @@
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
+/* Set a limit on recursion to avoid stack overflow. */
+#define MAX_TEST_RECURSION_DEPTH       100
+
 /* A simple implementation for signed numbers. */
 static int
 grub_strtosl (char *arg, const char ** const end, int base)
@@ -150,7 +153,7 @@ get_fileinfo (char *path, struct test_parse_ctx *ctx)
 
 /* Parse a test expression starting from *argn. */
 static int
-test_parse (char **args, int *argn, int argc)
+test_parse (char **args, int *argn, int argc, int *depth)
 {
   struct test_parse_ctx ctx = {
     .and = 1,
@@ -387,13 +390,24 @@ test_parse (char **args, int *argn, int argc)
       if (grub_strcmp (args[*argn], ")") == 0)
        {
          (*argn)++;
+         if (*depth > 0)
+           (*depth)--;
+
          return ctx.or || ctx.and;
        }
       /* Recursively invoke if parenthesis. */
       if (grub_strcmp (args[*argn], "(") == 0)
        {
          (*argn)++;
-         update_val (test_parse (args, argn, argc), &ctx);
+
+         if (++(*depth) > MAX_TEST_RECURSION_DEPTH)
+           {
+             grub_error (GRUB_ERR_OUT_OF_RANGE, N_("max recursion depth 
exceeded"));
+             depth--;
+             return ctx.or || ctx.and;
+           }
+
+         update_val (test_parse (args, argn, argc, depth), &ctx);
          continue;
        }
 
@@ -428,11 +442,12 @@ grub_cmd_test (grub_command_t cmd __attribute__ 
((unused)),
               int argc, char **args)
 {
   int argn = 0;
+  int depth = 0;
 
   if (argc >= 1 && grub_strcmp (args[argc - 1], "]") == 0)
     argc--;
 
-  return test_parse (args, &argn, argc) ? GRUB_ERR_NONE
+  return test_parse (args, &argn, argc, &depth) ? GRUB_ERR_NONE
     : grub_error (GRUB_ERR_TEST_FAILURE, N_("false"));
 }
 
-- 
2.11.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to