grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Proposal v2: fs/iso9660: Prevent skipping CE or ST at start of conti


From: Lidong Chen
Subject: Re: Proposal v2: fs/iso9660: Prevent skipping CE or ST at start of continuation area
Date: Mon, 9 Jan 2023 07:34:06 +0000

Hi Thomas,

> On Jan 6, 2023, at 8:00 AM, Thomas Schmitt <scdbackup@gmx.net> wrote:
> 
> Hi,
> 
> i wrote on Fri, 16 Dec 2022 10:42:13 +0100:
>>> If processing of a SUSP CE entry leads to a continuation area which begins
>>> by entry CE or ST, then these entries were skipped without interpretation.
>>> In case of CE this would lead to premature end of processing the SUSP
>>> entries of the file.
>>> In case of ST this could cause following non-SUSP bytes to be interpreted
>>> as SUSP entries.
>>> [...]
>>>            }
>>> 
>>>          entry = (struct grub_iso9660_susp_entry *) sua;
>>> +
>>> +         if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0
>>> +             || grub_strncmp ((char *) entry->sig, "ST", 2) == 0)
>>> +             /* The hook function will not process CE or ST.
>>> +                Advancing to the next entry would skip them. */
>>> +             continue;
>>>        }
>>> 
>>>       if (hook (entry, hook_arg))
> 
> I wrote on Fri, 16 Dec 2022 13:57:04 +0100:
>>> i realize that my previous proposal opens a possibility for regression
>>> with a very bad ISO image.
>>> The danger is in an endless loop by a CE entry which points to itself.
>>> [...] So i now propose:
>>>            }
>>> 
>>>          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)
>>> +           {
>>> +             ce = (struct grub_iso9660_susp_ce *) entry;
>>> +             if (ce_block
>>> +                 != grub_le_to_cpu32 (ce->blk) << GRUB_ISO9660_LOG2_BLKSZ
>>> +                 || off != grub_le_to_cpu32 (ce->off))
>>> +               continue;
>>> +             /* Ending up here indicates an endless loop by self reference.
>>> +                So skip this bad CE as was done before december 2022. */
>>> +           }
>>> +         if (grub_strncmp ((char *) entry->sig, "ST", 2) == 0)
>>> +           break;
>>>        }
>>> 
>>>       if (hook (entry, hook_arg))
> 
> Lidong Chen wrote:
>> In the original the CE code, ‘off’ and ‘ce_block’ were assigned with 
>> the values (highlighted below) that the above suggested fix tries to 
>> check against. So, it looks like it will never end here.
>>         off = grub_le_to_cpu32 (ce->off);
>>         ce_block = grub_le_to_cpu32 (ce->blk) << GRUB_ISO9660_LOG2_BLKSZ;
> 
> This happens before "entry" gets pointed to newly allocated memory which
> then gets filled with the data from the continuation area
> 
>          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;
> 
> Afterwards my proposed check shall peek ahead whether the suspicious new
> CE at the begin of this continuation area is a simple hoax which points
> to itself.
> 

Thanks for the clarification. I created a new patch for the fix and added you 
as the “Signed-off-by”.
My question is how to test it.  

The issues addressed by the other 4 patches were found by fuzzing. I restarted 
the fuzzing on 
those 4 patches, there was no crashes and hangs found. So, the patches fixed 
the issues.

For the new patch, since it wasn’t found by the fuzzer, I am not sure how to 
test it. 

> ------------------------------------------------------------------------
> 
> But meanwhile i think that a full fledged test for an endless loop is
> more appropriate.
> E.g. by a counter which breaks the loop:
> 
> --- grub-core/fs/iso9660.c.lidong_chen_patch_2        2022-12-15 
> 11:46:50.654295924 +0100
> +++ grub-core/fs/iso9660.c.lidong_chen_patch_2_ce_fix_v3      2023-01-06 
> 16:31:30.226698552 +0100
> @@ -176,6 +176,8 @@ enum
>     FLAG_MORE_EXTENTS = 0x80
>   };
> 
> +#define GRUB_ISO9660_MAX_CE_HOPS     1000000
> +
> static grub_dl_t my_mod;
> 
> 
> @@ -270,6 +272,7 @@ grub_iso9660_susp_iterate (grub_fshelp_n
>   char *sua;
>   struct grub_iso9660_susp_entry *entry;
>   grub_err_t err;
> +  int ce_counter = 0;
> 
>   if (sua_size <= 0)
>     return GRUB_ERR_NONE;
> @@ -307,6 +310,13 @@ grub_iso9660_susp_iterate (grub_fshelp_n
>         struct grub_iso9660_susp_ce *ce;
>         grub_disk_addr_t ce_block;
> 
> +       if (++ce_counter > GRUB_ISO9660_MAX_CE_HOPS)
> +         {
> +           grub_free (sua);
> +           return grub_error (GRUB_ERR_BAD_FS,
> +                              "suspecting endless CE loop");
> +         }
> +
>         ce = (struct grub_iso9660_susp_ce *) entry;
>         sua_size = grub_le_to_cpu32 (ce->len);
>         off = grub_le_to_cpu32 (ce->off);

I compiled the changes.  I have the same question for the testing changes.

Thanks,
Lidong

> 
> I don't add a signed-off-by because this is uncompiled and still needs
> thought about the maximum number of continuation hops and the reaction
> to a detected overflow of that number. Who does that work is the author
> of the patch.
> (1 million suffices for 2048 million bytes of SUSP data per file.
> You could silently bail out if this number is surpassed.)
> 
> 
> The check would be a separate patch, accompanied by my proposal v1 which
> then would need no own safety check:
> 
> @@ -336,6 +346,12 @@ grub_iso9660_susp_iterate (grub_fshelp_n
>           }
> 
>         entry = (struct grub_iso9660_susp_entry *) sua;
> +
> +       if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0
> +           || grub_strncmp ((char *) entry->sig, "ST", 2) == 0)
> +           /* The hook function will not process CE or ST.
> +              Advancing to the next entry would skip them. */
> +           continue;
>       }
> 
>       if (hook (entry, hook_arg))
> 
> (This one is already signed off by me.)
> 
> 
> Have a nice dday :)
> 
> Thomas
> 


reply via email to

[Prev in Thread] Current Thread [Next in Thread]