]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
fs/iso9660: Delay CE hop until end of current SUSP area
authorThomas Schmitt <scdbackup@gmx.net>
Tue, 7 Mar 2023 16:56:50 +0000 (17:56 +0100)
committerDaniel Kiper <daniel.kiper@oracle.com>
Thu, 13 Apr 2023 12:45:47 +0000 (14:45 +0200)
The SUSP specs demand that the reading of the next SUSP area which is
depicted by a CE entry shall be delayed until reading of the current
SUSP area is completed. Up to now GRUB immediately ends reading of the
current area and loads the new one. So, buffer the parameters of a found
CE entry and perform checks and reading of new data only after the
reader loop has ended.

Signed-off-by: Thomas Schmitt <scdbackup@gmx.net>
Tested-by: Lidong Chen <lidong.chen@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
grub-core/fs/iso9660.c

index acccf5fc7ce4af23b0c5c58a7bb8e95208b1aa5d..8c348b59a5e4e66c56e8b4fd31cbbce781b06604 100644 (file)
@@ -272,6 +272,9 @@ grub_iso9660_susp_iterate (grub_fshelp_node_t node, grub_off_t off,
   struct grub_iso9660_susp_entry *entry;
   grub_err_t err;
   int ce_counter = 0;
+  grub_ssize_t ce_sua_size = 0;
+  grub_off_t ce_off;
+  grub_disk_addr_t ce_block;
 
   if (sua_size <= 0)
     return GRUB_ERR_NONE;
@@ -293,6 +296,7 @@ grub_iso9660_susp_iterate (grub_fshelp_node_t node, grub_off_t off,
 
   entry = (struct grub_iso9660_susp_entry *) sua;
 
+ next_susp_area:
   while (entry->len > 0)
     {
       /* Ensure the entry is within System Use Area. */
@@ -307,55 +311,21 @@ grub_iso9660_susp_iterate (grub_fshelp_node_t node, grub_off_t off,
       if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0)
        {
          struct grub_iso9660_susp_ce *ce;
-         grub_disk_addr_t ce_block;
 
-         if (++ce_counter > GRUB_ISO9660_MAX_CE_HOPS)
+         if (ce_sua_size > 0)
            {
              grub_free (sua);
              return grub_error (GRUB_ERR_BAD_FS,
-                                "suspecting endless CE loop");
+                                "more than one CE entry in SUSP area");
            }
 
+         /* Buffer CE parameters for use after the end of this loop. */
          ce = (struct grub_iso9660_susp_ce *) entry;
-         sua_size = grub_le_to_cpu32 (ce->len);
-         off = grub_le_to_cpu32 (ce->off);
+         ce_sua_size = grub_le_to_cpu32 (ce->len);
+         ce_off = grub_le_to_cpu32 (ce->off);
          ce_block = grub_le_to_cpu32 (ce->blk) << GRUB_ISO9660_LOG2_BLKSZ;
-
-         if (sua_size <= 0)
-           break;
-
-         if (sua_size < GRUB_ISO9660_SUSP_HEADER_SZ)
-           {
-             grub_free (sua);
-             return grub_error (GRUB_ERR_BAD_FS,
-                                "invalid continuation area in CE entry");
-           }
-
-         grub_free (sua);
-         sua = grub_malloc (sua_size);
-         if (!sua)
-           return grub_errno;
-
-         /* Load a part of the System Usage Area.  */
-         err = grub_disk_read (node->data->disk, ce_block, off,
-                               sua_size, sua);
-         if (err)
-           {
-             grub_free (sua);
-             return err;
-           }
-
-         entry = (struct grub_iso9660_susp_entry *) sua;
-         /*
-          * The hook function will not process CE or ST.
-          * Advancing to the next entry would skip them.
-          */
-         if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0
-             || grub_strncmp ((char *) entry->sig, "ST", 2) == 0)
-           continue;
        }
-
-      if (hook (entry, hook_arg))
+      else if (hook (entry, hook_arg))
        {
          grub_free (sua);
          return 0;
@@ -367,6 +337,38 @@ grub_iso9660_susp_iterate (grub_fshelp_node_t node, grub_off_t off,
         break;
     }
 
+  if (ce_sua_size > 0)
+    {
+      /* Load the next System Use Area by buffered CE entry parameters. */
+      if (++ce_counter > GRUB_ISO9660_MAX_CE_HOPS)
+       {
+         grub_free (sua);
+         return grub_error (GRUB_ERR_BAD_FS, "suspecting endless CE loop");
+       }
+      if (ce_sua_size < GRUB_ISO9660_SUSP_HEADER_SZ)
+       {
+         grub_free (sua);
+         return grub_error (GRUB_ERR_BAD_FS, "invalid continuation area in CE entry");
+       }
+
+      grub_free (sua);
+      sua = grub_malloc (ce_sua_size);
+      if (!sua)
+       return grub_errno;
+
+      err = grub_disk_read (node->data->disk, ce_block, ce_off, ce_sua_size, sua);
+      if (err)
+       {
+         grub_free (sua);
+         return err;
+       }
+      entry = (struct grub_iso9660_susp_entry *) sua;
+      sua_size = ce_sua_size;
+      ce_sua_size = 0;
+
+      goto next_susp_area;
+    }
+
   grub_free (sua);
   return 0;
 }