Re: [PATCH 2/2] Fix menu entry selection based on ID and title

2024-04-22 Thread Vladimir 'phcoder' Serbinenko
I'm unsure whether an id starting with a numeral should be considered valid
at all. Variable named 0x would be incorrect

Le lun. 22 avr. 2024, 17:57, Marek Marczykowski-Górecki <
marma...@invisiblethingslab.com> a écrit :

> From: Peter Jones 
>
> Currently if grub_strtoul(saved_entry_value, NULL, 0) does not return an
> error, we assume the value it has produced is a correct index into our
> menu entry list, and do not try to interpret the value as the "id" or
> "title" .  In cases where "id" or "title" start with a numeral, this
> makes them impossible to use as selection criteria.
>
> This patch splits the search into three phases - matching id, matching
> title, and only once those have been exhausted, trying to interpret the
> ID as a numeral.  In that case, we also require that the entire string
> is numeric, not merely a string with leading numeric characters.
>
> Resolves: rhbz#1640979
>
> Signed-off-by: Peter Jones 
> [javierm: fix menu entry selection based on title]
> Signed-off-by: Javier Martinez Canillas 
> ---
>  grub-core/normal/menu.c | 141 -
>  1 file changed, 71 insertions(+), 70 deletions(-)
>
> diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c
> index 6444ee6..b0cfa32 100644
> --- a/grub-core/normal/menu.c
> +++ b/grub-core/normal/menu.c
> @@ -164,12 +164,12 @@ grub_menu_set_timeout (int timeout)
>  }
>
>  static int
> -menuentry_eq (const char *id, const char *spec)
> +menuentry_eq (const char *id, const char *spec, int limit)
>  {
>const char *ptr1, *ptr2;
>ptr1 = id;
>ptr2 = spec;
> -  while (1)
> +  while (limit == -1 || ptr1 - id <= limit)
>  {
>if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
> return ptr2 - spec;
> @@ -178,7 +178,11 @@ menuentry_eq (const char *id, const char *spec)
>if (*ptr2 == '>')
> ptr2++;
>if (*ptr1 != *ptr2)
> -   return 0;
> +   {
> + if (limit > -1 && ptr1 - id == limit && !*ptr1 &&
> grub_isspace(*ptr2))
> +   return ptr1 -id -1;
> + return 0;
> +   }
>if (*ptr1 == 0)
> return ptr1 - id;
>ptr1++;
> @@ -187,6 +191,58 @@ menuentry_eq (const char *id, const char *spec)
>return 0;
>  }
>
> +static int
> +get_entry_number_helper(grub_menu_t menu,
> +   const char * const val, const char ** const tail)
> +{
> +  /* See if the variable matches the title of a menu entry.  */
> +  int entry = -1;
> +  grub_menu_entry_t e;
> +  int i;
> +
> +  for (i = 0, e = menu->entry_list; e; i++)
> +{
> +  int l = 0;
> +  while (val[l] && !grub_isspace(val[l]))
> +   l++;
> +
> +  if (menuentry_eq (e->id, val, l))
> +   {
> + if (tail)
> +   *tail = val + l;
> + return i;
> +   }
> +  e = e->next;
> +}
> +
> +  for (i = 0, e = menu->entry_list; e; i++)
> +{
> +
> +  if (menuentry_eq (e->title, val, -1))
> +   {
> + if (tail)
> +   *tail = NULL;
> + return i;
> +   }
> +  e = e->next;
> +}
> +
> +  if (tail)
> +*tail = NULL;
> +
> +  entry = (int) grub_strtoul (val, tail, 0);
> +  if (grub_errno == GRUB_ERR_BAD_NUMBER ||
> +  (*tail && **tail && !grub_isspace(**tail)))
> +{
> +  entry = -1;
> +  if (tail)
> +   *tail = NULL;
> +  grub_errno = GRUB_ERR_NONE;
> +}
> +
> +  return entry;
> +}
> +
>  /* Get the first entry number from the value of the environment variable
> NAME,
> which is a space-separated list of non-negative integers.  The entry
> number
> which is returned is stripped from the value of NAME.  If no entry
> number
> @@ -196,7 +252,6 @@ get_and_remove_first_entry_number (grub_menu_t menu,
> const char *name)
>  {
>const char *val, *tail;
>int entry;
> -  int sz = 0;
>
>val = grub_env_get (name);
>if (! val)
> @@ -204,50 +259,24 @@ get_and_remove_first_entry_number (grub_menu_t menu,
> const char *name)
>
>grub_error_push ();
>
> -  entry = (int) grub_strtoul (val, , 0);
> -
> -  if (grub_errno == GRUB_ERR_BAD_NUMBER)
> -{
> -  /* See if the variable matches the title of a menu entry.  */
> -  grub_menu_entry_t e = menu->entry_list;
> -  int i;
> -
> -  for (i = 0; e; i++)
> -   {
> - sz = menuentry_eq (e->title, val);
> - if (sz < 1)
> -   sz = menuentry_eq (e->id, val);
> -
> - if (sz >= 1)
> -   {
> - entry = i;
> - break;
> -   }
> - e = e->next;
> -   }
> +  entry = get_entry_number_helper(menu, val, );
> +  if (!(*tail == 0 || grub_isspace(*tail)))
> +entry = -1;
>
> -  if (sz > 0)
> -   grub_errno = GRUB_ERR_NONE;
> -
> -  if (! e)
> -   entry = -1;
> -}
> -
> -  if (grub_errno == GRUB_ERR_NONE)
> +  if (entry >= 0)
>  {
> -  if (sz > 0)
> -   tail += sz;
> -
>/* Skip whitespace to find the next entry.  */
>while (*tail && grub_isspace 

[PATCH 2/2] Fix menu entry selection based on ID and title

2024-04-22 Thread Marek Marczykowski-Górecki
From: Peter Jones 

Currently if grub_strtoul(saved_entry_value, NULL, 0) does not return an
error, we assume the value it has produced is a correct index into our
menu entry list, and do not try to interpret the value as the "id" or
"title" .  In cases where "id" or "title" start with a numeral, this
makes them impossible to use as selection criteria.

This patch splits the search into three phases - matching id, matching
title, and only once those have been exhausted, trying to interpret the
ID as a numeral.  In that case, we also require that the entire string
is numeric, not merely a string with leading numeric characters.

Resolves: rhbz#1640979

Signed-off-by: Peter Jones 
[javierm: fix menu entry selection based on title]
Signed-off-by: Javier Martinez Canillas 
---
 grub-core/normal/menu.c | 141 -
 1 file changed, 71 insertions(+), 70 deletions(-)

diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c
index 6444ee6..b0cfa32 100644
--- a/grub-core/normal/menu.c
+++ b/grub-core/normal/menu.c
@@ -164,12 +164,12 @@ grub_menu_set_timeout (int timeout)
 }
 
 static int
-menuentry_eq (const char *id, const char *spec)
+menuentry_eq (const char *id, const char *spec, int limit)
 {
   const char *ptr1, *ptr2;
   ptr1 = id;
   ptr2 = spec;
-  while (1)
+  while (limit == -1 || ptr1 - id <= limit)
 {
   if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
return ptr2 - spec;
@@ -178,7 +178,11 @@ menuentry_eq (const char *id, const char *spec)
   if (*ptr2 == '>')
ptr2++;
   if (*ptr1 != *ptr2)
-   return 0;
+   {
+ if (limit > -1 && ptr1 - id == limit && !*ptr1 && grub_isspace(*ptr2))
+   return ptr1 -id -1;
+ return 0;
+   }
   if (*ptr1 == 0)
return ptr1 - id;
   ptr1++;
@@ -187,6 +191,58 @@ menuentry_eq (const char *id, const char *spec)
   return 0;
 }
 
+static int
+get_entry_number_helper(grub_menu_t menu,
+   const char * const val, const char ** const tail)
+{
+  /* See if the variable matches the title of a menu entry.  */
+  int entry = -1;
+  grub_menu_entry_t e;
+  int i;
+
+  for (i = 0, e = menu->entry_list; e; i++)
+{
+  int l = 0;
+  while (val[l] && !grub_isspace(val[l]))
+   l++;
+
+  if (menuentry_eq (e->id, val, l))
+   {
+ if (tail)
+   *tail = val + l;
+ return i;
+   }
+  e = e->next;
+}
+
+  for (i = 0, e = menu->entry_list; e; i++)
+{
+
+  if (menuentry_eq (e->title, val, -1))
+   {
+ if (tail)
+   *tail = NULL;
+ return i;
+   }
+  e = e->next;
+}
+
+  if (tail)
+*tail = NULL;
+
+  entry = (int) grub_strtoul (val, tail, 0);
+  if (grub_errno == GRUB_ERR_BAD_NUMBER ||
+  (*tail && **tail && !grub_isspace(**tail)))
+{
+  entry = -1;
+  if (tail)
+   *tail = NULL;
+  grub_errno = GRUB_ERR_NONE;
+}
+
+  return entry;
+}
+
 /* Get the first entry number from the value of the environment variable NAME,
which is a space-separated list of non-negative integers.  The entry number
which is returned is stripped from the value of NAME.  If no entry number
@@ -196,7 +252,6 @@ get_and_remove_first_entry_number (grub_menu_t menu, const 
char *name)
 {
   const char *val, *tail;
   int entry;
-  int sz = 0;
 
   val = grub_env_get (name);
   if (! val)
@@ -204,50 +259,24 @@ get_and_remove_first_entry_number (grub_menu_t menu, 
const char *name)
 
   grub_error_push ();
 
-  entry = (int) grub_strtoul (val, , 0);
-
-  if (grub_errno == GRUB_ERR_BAD_NUMBER)
-{
-  /* See if the variable matches the title of a menu entry.  */
-  grub_menu_entry_t e = menu->entry_list;
-  int i;
-
-  for (i = 0; e; i++)
-   {
- sz = menuentry_eq (e->title, val);
- if (sz < 1)
-   sz = menuentry_eq (e->id, val);
-
- if (sz >= 1)
-   {
- entry = i;
- break;
-   }
- e = e->next;
-   }
+  entry = get_entry_number_helper(menu, val, );
+  if (!(*tail == 0 || grub_isspace(*tail)))
+entry = -1;
 
-  if (sz > 0)
-   grub_errno = GRUB_ERR_NONE;
-
-  if (! e)
-   entry = -1;
-}
-
-  if (grub_errno == GRUB_ERR_NONE)
+  if (entry >= 0)
 {
-  if (sz > 0)
-   tail += sz;
-
   /* Skip whitespace to find the next entry.  */
   while (*tail && grub_isspace (*tail))
tail++;
-  grub_env_set (name, tail);
+  if (*tail)
+   grub_env_set (name, tail);
+  else
+   grub_env_unset (name);
 }
   else
 {
   grub_env_unset (name);
   grub_errno = GRUB_ERR_NONE;
-  entry = -1;
 }
 
   grub_error_pop ();
@@ -524,6 +553,7 @@ static int
 get_entry_number (grub_menu_t menu, const char *name)
 {
   const char *val;
+  const char *tail;
   int entry;
 
   val = grub_env_get (name);
@@ -531,38 +561,9 @@ get_entry_number (grub_menu_t menu, const char *name)