On 16.10.2007 19:30, Uwe Hermann wrote:
> On Sat, Oct 13, 2007 at 07:52:55PM +0200, Carl-Daniel Hailfinger wrote:
>   
>> Fix a corner case access to uninitialized memory (NULL pointer
>> dereference or worse) in case the archive length is exactly
>> sizeof(struct lar_header). Such an archive is invalid because the
>> filename directly after the LAR header is always dereferenced and has to
>> be at least 1 byte in the "empty filename" case (only terminating \0).
>> Improve LAR code documentation and reorder variables in one assignment
>> to make the code more obvious and readable. This will help people
>> understand what the code does when they look at it half a year from now.
>>
>> Signed-off-by: Carl-Daniel Hailfinger <[EMAIL PROTECTED]>
>> ---
>>
>> Index: LinuxBIOSv3/include/lar.h
>> ===================================================================
>> --- LinuxBIOSv3/include/lar.h        (Revision 505)
>> +++ LinuxBIOSv3/include/lar.h        (Arbeitskopie)
>> @@ -52,10 +52,9 @@
>>  
>>  #include <types.h>
>>  
>> -/* see note in lib/lar.c as to why this is ARCHIVE and not LARCHIVE */
>>  #define MAGIC "LARCHIVE"
>>  #define MAX_PATHLEN 1024
>> -/* NOTE -- This and the user-mode lar.h are NOT IN SYNC. Be careful. */
>> +/* NOTE -- This and the user-mode lar.h may NOT be IN SYNC. Be careful. */
>>     
>
> IN SYNC -> in sync (the NOT should stay all-caps)
>   

OK

>>  struct lar_header {
>>      char magic[8];
>>      u32 len;
>> Index: LinuxBIOSv3/lib/lar.c
>> ===================================================================
>> --- LinuxBIOSv3/lib/lar.c    (Revision 505)
>> +++ LinuxBIOSv3/lib/lar.c    (Arbeitskopie)
>> @@ -49,7 +49,7 @@
>>   * Given a file name in the LAR , search for it, and fill out a return 
>> struct with the 
>>   * result. 
>>   * @param archive A descriptor for current archive. This is actually a 
>> mem_file type, 
>> - *    which is a machine-dependent representation of hte actual archive. In 
>> particular, 
>> + *    which is a machine-dependent representation of the actual archive. In 
>> particular, 
>>     
>
> Yep.
>   
>>   *    things like u32 are void * in the mem_file struct. 
>>   * @param filename filename to find
>>   * @param result pointer to mem_file struct which is filled in if the file 
>> is found
>> @@ -65,30 +65,43 @@
>>      printk(BIOS_SPEW, "LAR: Start %p len 0x%x\n", archive->start,
>>             archive->len);
>>  
>> -    if (archive->len < sizeof(struct lar_header))
>> -            printk(BIOS_ERR, "Error: truncated archive (%d bytes); minimum 
>> possible size is %d bytes\n", 
>> -                    archive->len, sizeof(struct lar_header));
>> +    /* Why check for sizeof(struct lar_header) + 1? The code below expects
>> +     * a filename to follow directly after the LAR header and will
>> +     * dereference the address directly after the header. However, if
>> +     * archive->len == sizeof(struct lar_header), printing the filename
>> +     * will dereference memory outside the archive. Without looking at the
>> +     * filename, the only thing we can check is that there is at least room
>> +     * for an empty filename (only the terminating \0).
>> +     */
>> +    if (archive->len < sizeof(struct lar_header) + 1)
>> +            printk(BIOS_ERR, "Error: truncated archive (%d bytes); minimum"
>> +                    " possible size is %d bytes\n",
>> +                    archive->len, sizeof(struct lar_header) + 1);
>>     
>
> Is this build-tested and tested with on a real lar file etc?
>   

It is build-tested. It should not fail with any real LAR file unless
that LAR file is deliberately truncated and therefore invalid.

>> -    /* Getting this for loop right is harder than it looks. All quantities 
>> are 
>> -      * unsigned. The LAR stretches from (e.g.) 0xfff0000 for 0x100000 
>> -      * bytes, i.e. to address ZERO. 
>> -      * As a result, 'walk', can wrap to zero and keep going (this has been 
>> -      * seen in practice). Recall that these are unsigned; walk can 
>> -      * wrap to zero; so, question, when is walk less than any of these:
>> -      * archive->start
>> -      * Answer: once walk wraps to zero, it is < archive->start
>> -      * archive->start + archive->len
>> -      * archive->start + archive->len  - 1
>> -      * Answer: In the case that archive->start + archive->len == 0, ALWAYS!
>> -      * A lot of expressions have been tried and all have been wrong. 
>> -      * So what would work? Simple:
>> -      * test for walk < archive->start + archive->len - 1 to cover the case
>> -      *     that the archive does NOT occupy ALL of the top of memory and 
>> -      *     wrap to zero; 
>> -      * and test for walk >= archive->start, 
>> -      * to cover the case that you wrapped to zero. 
>> -      * Unsigned pointer arithmetic that wraps to zero can be messy.
>> -      */
>> +    /* Getting this for loop right is harder than it looks. All quantities
>> +     * are unsigned. The LAR stretches from (e.g.) 0xfff0000 for 0x100000
>> +     * bytes, i.e. to address ZERO.
>> +     * As a result, 'walk', can wrap to zero and keep going (this has been
>> +     * seen in practice). Recall that these are unsigned; walk can
>> +     * wrap to zero; so, question, when is walk less than any of these:
>> +     * archive->start
>> +     * Answer: once walk wraps to zero, it is < archive->start
>> +     * archive->start + archive->len
>> +     * archive->start + archive->len  - 1
>> +     * Answer: In the case that archive->start + archive->len == 0, ALWAYS!
>> +     * A lot of expressions have been tried and all have been wrong.
>> +     * So what would work? Simple:
>> +     * test for walk < archive->start + archive->len - sizeof(lar_header)
>> +     *      to cover the case that the archive does NOT occupy ALL of the
>> +     *      top of memory and wrap to zero; RESIST the temptation to change
>> +     *      that comparison to <= because if a header did terminate the
>> +     *      archive, the filename (stored directly after the header) would
>> +     *      be outside the archive and you'd get a nice NULL pointer for
>> +     *      the filename
>> +     * and test for walk >= archive->start,
>> +     *      to cover the case that you wrapped to zero.
>> +     * Unsigned pointer arithmetic that wraps to zero can be messy.
>> +     */
>>      for (walk = archive->start;
>>           (walk < (char *)(archive->start + archive->len - sizeof(struct 
>> lar_header))) && 
>>                      (walk >= (char *)archive->start); walk += 16) {
>> @@ -98,7 +111,7 @@
>>              header = (struct lar_header *)walk;
>>              fullname = walk + sizeof(struct lar_header);
>>  
>> -            printk(BIOS_SPEW, "LAR: search for %s\n", fullname);
>> +            printk(BIOS_SPEW, "LAR: seen member %s\n", fullname);
>>              // FIXME: check checksum
>>  
>>              if (strcmp(fullname, filename) == 0) {
>> @@ -115,11 +128,20 @@
>>                              result->compression, result->entry, 
>> result->loadaddress);
>>                      return 0;
>>              }
>> -            // skip file
>> -            walk += (ntohl(header->len) + ntohl(header->offset) -
>> -                    1) & 0xfffffff0;
>> +            /* skip file:
>> +             * The next iteration of the for loop will add 16 to walk, so
>> +             * we now add offset (from header start to data start) and len
>> +             * (member length), subtract 1 (to get the address of the last
>> +             * byte of the member) and round this down to the next 16 byte
>> +             * boundary.
>> +             * In the case of consecutive archive members with header-
>> +             * before-member structure, the next iteration of the loop will
>> +             * start exactly at the beginning of the next header.
>> +             */
>> +            walk += (ntohl(header->offset) + ntohl(header->len) - 1)
>> +                     & 0xfffffff0;
>>      }
>> -    printk(BIOS_SPEW, "NO FILE FOUND\n");
>> +    printk(BIOS_SPEW, "LAR: NO FILE FOUND\n");
>>     
>
>       printk(BIOS_SPEW, "LAR: NO FILE FOUND!\n");
>   
OK

>>      return 1;
>>  }
>>  
>> @@ -157,7 +179,7 @@
>>   * the loadaddress pointer in the mem_file struct. 
>>   * @param archive A descriptor for current archive.
>>   * @param filename filename to find
>> - * returns 0 on success, -1 otherwise
>> + * returns entry on success, (void*)-1 otherwise
>>   */
>>  
>>  void *load_file(struct mem_file *archive, const char *filename)
>> Index: LinuxBIOSv3/util/lar/lar.h
>> ===================================================================
>> --- LinuxBIOSv3/util/lar/lar.h       (Revision 505)
>> +++ LinuxBIOSv3/util/lar/lar.h       (Arbeitskopie)
>> @@ -61,13 +61,15 @@
>>  typedef uint32_t u32;
>>  typedef uint8_t  u8;
>>  
>> -/* NOTE -- This and the linuxbios lar.h are NOT IN SYNC. Be careful. */
>> +/* NOTE -- This and the user-mode lar.h may NOT be IN SYNC. Be careful. */
>>     
>
> IN SYNC -> in sync
>   
OK

>>  struct lar_header {
>>      char magic[8];
>>      u32 len;
>>      u32 reallen;
>>      u32 checksum;
>>      u32 compchecksum;
>> +    /* Filenames are limited to 2^31-1-sizeof(lar_header)-1 bytes.
>> +     * "Nobody will ever need more than 640k" */
>>      u32 offset;
>>      /* Compression:
>>       * 0 = no compression
>> Index: LinuxBIOSv3/arch/x86/stage1.c
>> ===================================================================
>> --- LinuxBIOSv3/arch/x86/stage1.c    (Revision 505)
>> +++ LinuxBIOSv3/arch/x86/stage1.c    (Arbeitskopie)
>> @@ -69,7 +69,7 @@
>>  }
>>  
>>  /*
>> - * This function is called from assembler code whith its argument on the
>> + * This function is called from assembler code with its argument on the
>>   * stack. Force the compiler to generate always correct code for this case.
>>   */
>>  void __attribute__((stdcall)) stage1_main(u32 bist)
>> @@ -140,7 +140,7 @@
>>      } else {
>>              printk(BIOS_DEBUG, "Choosing fallback boot.\n");
>>              ret = execute_in_place(&archive, "fallback/initram");
>> -            /* Try a normal boot if fallback doesn't exists in the lar.
>> +            /* Try a normal boot if fallback doesn't exist in the lar.
>>               * TODO: There are other ways to do this.
>>               * It could be ifdef or the boot flag could be forced.
>>               */
>>     
>
>
> Looks good otherwise. With the above fixes:
>
> Acked-by: Uwe Hermann <[EMAIL PROTECTED]>
>   

Thanks, will fix and commit.

Carl-Daniel

-- 
linuxbios mailing list
linuxbios@linuxbios.org
http://www.linuxbios.org/mailman/listinfo/linuxbios

Reply via email to