]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[prefix] Use CRC32 to verify each block prior to decompression
authorMichael Brown <mcb30@ipxe.org>
Wed, 23 Mar 2016 13:41:17 +0000 (13:41 +0000)
committerMichael Brown <mcb30@ipxe.org>
Thu, 24 Mar 2016 16:52:26 +0000 (16:52 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/x86/prefix/libprefix.S
src/arch/x86/prefix/unlzma.S
src/util/zbin.c

index 425f5148469b28f60951c85e376f28b4dd3bd264..533be981e51fef950a849260cd486d2193ee43be 100644 (file)
@@ -341,6 +341,7 @@ zero_bytes:
  * Returns:
  *   %esi : next source physical address
  *   %edi : next destination physical address
+ *   CF : as returned by memcpy()-like function
  * Corrupts:
  *   None
  ****************************************************************************
@@ -356,6 +357,7 @@ process_bytes:
        pushl   %ebp
 
        /* Construct GDT on stack (since .prefix may not be writable) */
+       .equ    GDT_LEN, 0x20
        .equ    PM_DS, 0x18     /* Flat data segment */
        pushl   $0x00cf9300
        pushl   $0x0000ffff
@@ -369,7 +371,7 @@ process_bytes:
        pushw   $0xffff
        pushl   $0              /* Base and length */
        pushw   %ss
-       pushw   $0x1f
+       pushw   $( GDT_LEN - 1 )
        movzwl  %sp, %ebp
        shll    $4, 0x02(%bp)
        addl    %ebp, 0x02(%bp)
@@ -407,7 +409,9 @@ process_bytes:
 
        /* Return to (flat) real mode */
        movl    %cr0, %eax
+       pushfw
        andb    $0!CR0_PE, %al
+       popfw
        movl    %eax, %cr0
        lret
 2:     /* lret will ljmp to here */
@@ -433,7 +437,7 @@ process_bytes:
 
        /* Restore GDT */
        data32 lgdt -8(%bp)
-       addw    $( 8 /* saved GDT */ + ( PM_DS + 8 ) /* GDT on stack */ ), %sp
+       leaw    GDT_LEN(%bp), %sp
 
        /* Restore registers and return */
        popl    %ebp
@@ -461,6 +465,7 @@ process_bytes:
        call    *%bx
 
        /* Convert %ds:esi and %es:edi back to physical addresses */
+       pushfw
        xorl    %eax, %eax
        movw    %ds, %ax
        shll    $4, %eax
@@ -469,6 +474,7 @@ process_bytes:
        movw    %es, %ax
        shll    $4, %eax
        addl    %eax, %edi
+       popfw
 
        /* Restore registers and return */
        popw    %es
@@ -493,6 +499,7 @@ process_bytes:
  * Returns:
  *   %esi : next source physical address (will be a multiple of 16)
  *   %edi : next destination physical address (will be a multiple of 16)
+ *   CF set on failure
  * Corrupts:
  *   none
  ****************************************************************************
@@ -511,6 +518,7 @@ install_block:
        movw    $copy_bytes, %bx
 #endif
        call    process_bytes
+       jc      99f
 
        /* Zero .bss portion */
        negl    %ecx
@@ -522,9 +530,9 @@ install_block:
        addl    $0xf, %esi
        andl    $~0xf, %esi
        addl    $0xf, %edi
-       andl    $~0xf, %edi
+       andl    $~0xf, %edi /* Will also clear CF */
 
-       /* Restore registers and return */
+99:    /* Restore registers and return */
        popw    %bx
        popl    %ecx
        ret
@@ -730,6 +738,7 @@ install_prealloc:
        movl    $_text16_early_filesz, %ecx
        movl    $_text16_early_memsz, %edx
        call    install_block           /* .text16.early */
+       jc      install_block_death
        popl    %ecx                    /* Calculate offset to next block */
        subl    %esi, %ecx
        negl    %ecx
@@ -748,17 +757,8 @@ install_prealloc:
        pushw   $access_highmem
        lret
 1:     /* Die if we could not access high memory */
-       jnc     3f
-       movw    $a20_death_message, %si
-       xorw    %di, %di
-       call    print_message
-2:     jmp     2b
-       .section ".prefix.data.a20_death_message", "aw", @progbits
-a20_death_message:
-       .asciz  "\nHigh memory inaccessible - cannot continue\n"
-       .size   a20_death_message, . - a20_death_message
-       .previous
-3:
+       jc      access_highmem_death
+
 #endif
 
        /* Open payload (which may not yet be in memory) */
@@ -769,25 +769,7 @@ a20_death_message:
        pushw   $open_payload
        lret
 1:     /* Die if we could not access the payload */
-       jnc     3f
-       xorw    %di, %di
-       movl    %esi, %eax
-       call    print_hex_dword
-       call    print_space
-       movl    %ecx, %eax
-       call    print_hex_dword
-       movw    $payload_death_message, %si
-       call    print_message
-2:     /* Halt system */
-       cli
-       hlt
-       jmp     2b
-       .section ".prefix.data.payload_death_message", "aw", @progbits
-payload_death_message:
-       .asciz  "\nPayload inaccessible - cannot continue\n"
-       .size   payload_death_message, . - payload_death_message
-       .previous
-3:
+       jc      open_payload_death
 
        /* Calculate physical address of payload (i.e. first source) */
        testl   %esi, %esi
@@ -801,12 +783,14 @@ payload_death_message:
        movl    $_text16_late_filesz, %ecx
        movl    $_text16_late_memsz, %edx
        call    install_block           /* .text16.late */
+       jc      install_block_death
        progress "  .data16\n"
        movzwl  %bx, %edi
        shll    $4, %edi
        movl    $_data16_filesz, %ecx
        movl    $_data16_filesz, %edx   /* do not zero our temporary stack */
        call    install_block           /* .data16 */
+       jc      install_block_death
 
        /* Set up %ds for access to .data16 */
        movw    %bx, %ds
@@ -846,6 +830,7 @@ payload_death_message:
        movl    $_textdata_filesz, %ecx
        movl    $_textdata_memsz, %edx
        call    install_block
+       jc      install_block_death
        popl    %edi
 
 #endif /* KEEP_IT_REAL */
@@ -960,6 +945,52 @@ close_payload:
        .size   open_payload, . - open_payload
        .size   close_payload, . - close_payload
 
+       /* Report installation failure */
+       .section ".prefix.install_death", "ax", @progbits
+install_death:
+       pushw   %cs
+       popw    %ds
+       xorw    %di, %di
+       call    print_hex_dword
+       call    print_space
+       movl    %esi, %eax
+       call    print_hex_dword
+       call    print_space
+       movl    %ecx, %eax
+       call    print_hex_dword
+       movw    $install_death_message, %si
+       call    print_message
+2:     /* Halt system */
+       cli
+       hlt
+       jmp     2b
+       .size   install_death, . - install_death
+       .section ".prefix.data.install_death_message", "aw", @progbits
+install_death_message:
+       .asciz  "\nInstallation failed - cannot continue\n"
+       .size   install_death_message, . - install_death_message
+
+       /* Report failure to access high memory */
+       .section ".prefix.install_block_death", "ax", @progbits
+install_block_death:
+       movl    $0x1b101b10, %eax
+       jmp     install_death
+       .size   install_block_death, . - install_block_death
+
+       /* Report failure to access high memory */
+       .section ".prefix.access_highmem_death", "ax", @progbits
+access_highmem_death:
+       movl    $0x0a200a20, %eax
+       jmp     install_death
+       .size   access_highmem_death, . - access_highmem_death
+
+       /* Report failure to open payload */
+       .section ".prefix.open_payload_death", "ax", @progbits
+open_payload_death:
+       xorl    %eax, %eax
+       jmp     install_death
+       .size   open_payload_death, . - open_payload_death
+
 /****************************************************************************
  * uninstall
  *
index 8d4b3c1a87ae9c33a8538ff02aaa6c8ba411223e..ce18c756fa655cf03bab8be78166eca307429c6e 100644 (file)
@@ -58,6 +58,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
        .code32
 #endif /* CODE16 */
 
+#define CRCPOLY 0xedb88320
+#define CRCSEED 0xffffffff
+
 /****************************************************************************
  * Debugging
  ****************************************************************************
@@ -862,6 +865,44 @@ bcj_filter:
        ret
        .size   bcj_filter, . - bcj_filter
 
+/****************************************************************************
+ * Verify CRC32
+ *
+ * Parameters:
+ *   %ds:%esi : Start of compressed input data
+ *   %edx : Length of compressed input data (including CRC)
+ * Returns:
+ *   CF clear if CRC32 is zero
+ *   All other registers are preserved
+ * Corrupts:
+ *   %eax
+ *   %ebx
+ *   %ecx
+ *   %edx
+ *   %esi
+ ****************************************************************************
+ */
+verify_crc32:
+       /* Calculate CRC */
+       addl    %esi, %edx
+       movl    $CRCSEED, %ebx
+1:     ADDR32 lodsb
+       xorb    %al, %bl
+       movw    $8, %cx
+2:     rcrl    %ebx
+       jnc     3f
+       xorl    $CRCPOLY, %ebx
+3:     ADDR16 loop 2b
+       cmpl    %esi, %edx
+       jne     1b
+       /* Set CF if result is nonzero */
+       testl   %ebx, %ebx
+       jz      1f
+       stc
+1:     /* Return */
+       ret
+       .size   verify_crc32, . - verify_crc32
+
 /****************************************************************************
  * decompress (real-mode or 16/32-bit protected-mode near call)
  *
@@ -873,6 +914,7 @@ bcj_filter:
  * Returns:
  *   %ds:%esi - End of compressed input data
  *   %es:%edi - End of decompressed output data
+ *   CF set if CRC32 was incorrect
  *   All other registers are preserved
  *
  * NOTE: It would be possible to build a smaller version of the
@@ -888,6 +930,13 @@ decompress:
        pushl   %ecx
        pushl   %edx
        pushl   %ebp
+       /* Verify CRC32 */
+       ADDR32 lodsl
+       movl    %eax, %edx
+       pushl   %esi
+       call    verify_crc32
+       popl    %esi
+       jc      99f
        /* Allocate parameter block */
        subl    $sizeof__lzma_dec, %esp
        movl    %esp, %ebp
@@ -928,8 +977,11 @@ decompress:
        movl    out_start(%ebp), %esi
        call    bcj_filter
        popl    %esi
-       /* Restore registers and return */
+       /* Skip CRC */
+       ADDR32 lodsl
+       /* Free parameter block (and clear CF) */
        addl    $sizeof__lzma_dec, %esp
+99:    /* Restore registers and return */
        popl    %ebp
        popl    %edx
        popl    %ecx
index 1862a3827283a2e101883141a278f2423e0857e2..75fba583fc6163a8ab074fd00ba975ee6fa07ab4 100644 (file)
@@ -144,6 +144,7 @@ static int read_zinfo_file ( const char *filename,
 
 static int alloc_output_file ( size_t max_len, struct output_file *output ) {
        output->len = 0;
+       output->hdr_len = 0;
        output->max_len = ( max_len );
        output->buf = malloc ( max_len );
        if ( ! output->buf ) {
@@ -241,19 +242,41 @@ static void bcj_filter ( void *data, size_t len ) {
        };
 }
 
+#define CRCPOLY 0xedb88320
+#define CRCSEED 0xffffffff
+
+static uint32_t crc32_le ( uint32_t crc, const void *data, size_t len ) {
+       const uint8_t *src = data;
+       uint32_t mult;
+       unsigned int i;
+
+       while ( len-- ) {
+               crc ^= *(src++);
+               for ( i = 0 ; i < 8 ; i++ ) {
+                       mult = ( ( crc & 1 ) ? CRCPOLY : 0 );
+                       crc = ( ( crc >> 1 ) ^ mult );
+               }
+       }
+       return crc;
+}
+
 static int process_zinfo_pack ( struct input_file *input,
                                struct output_file *output,
                                union zinfo_record *zinfo ) {
        struct zinfo_pack *pack = &zinfo->pack;
        size_t offset = pack->offset;
        size_t len = pack->len;
+       size_t start_len;
        size_t packed_len = 0;
-       size_t remaining = ( output->max_len - output->len );
+       size_t remaining;
        lzma_options_lzma options;
        const lzma_filter filters[] = {
                { .id = LZMA_FILTER_LZMA1, .options = &options },
                { .id = LZMA_VLI_UNKNOWN }
        };
+       void *packed;
+       uint32_t *len32;
+       uint32_t *crc32;
 
        if ( ( offset + len ) > input->len ) {
                fprintf ( stderr, "Input buffer overrun on pack\n" );
@@ -261,6 +284,9 @@ static int process_zinfo_pack ( struct input_file *input,
        }
 
        output->len = align ( output->len, pack->align );
+       start_len = output->len;
+       len32 = ( output->buf + output->len );
+       output->len += sizeof ( *len32 );
        if ( output->len > output->max_len ) {
                fprintf ( stderr, "Output buffer overrun on pack\n" );
                return -1;
@@ -268,28 +294,34 @@ static int process_zinfo_pack ( struct input_file *input,
 
        bcj_filter ( ( input->buf + offset ), len );
 
+       packed = ( output->buf + output->len );
+       remaining = ( output->max_len - output->len );
        lzma_lzma_preset ( &options, LZMA_PRESET );
        options.lc = LZMA_LC;
        options.lp = LZMA_LP;
        options.pb = LZMA_PB;
        if ( lzma_raw_buffer_encode ( filters, NULL, ( input->buf + offset ),
-                                     len, ( output->buf + output->len ),
-                                     &packed_len, remaining ) != LZMA_OK ) {
+                                     len, packed, &packed_len,
+                                     remaining ) != LZMA_OK ) {
                fprintf ( stderr, "Compression failure\n" );
                return -1;
        }
-
-       if ( DEBUG ) {
-               fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n",
-                         offset, ( offset + len ), output->len,
-                         ( output->len + packed_len ) );
-       }
-
        output->len += packed_len;
+
+       crc32 = ( output->buf + output->len );
+       output->len += sizeof ( *crc32 );
        if ( output->len > output->max_len ) {
                fprintf ( stderr, "Output buffer overrun on pack\n" );
                return -1;
        }
+       *len32 = ( packed_len + sizeof ( *crc32 ) );
+       *crc32 = crc32_le ( CRCSEED, packed, packed_len );
+
+       if ( DEBUG ) {
+               fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx) crc %#08x\n",
+                         offset, ( offset + len ), start_len, output->len,
+                         *crc32 );
+       }
 
        return 0;
 }