From c4e8c40227ebb11361e066be96ed93927eedfbf4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Mar 2016 13:41:17 +0000 Subject: [PATCH] [prefix] Use CRC32 to verify each block prior to decompression Signed-off-by: Michael Brown --- src/arch/x86/prefix/libprefix.S | 99 ++++++++++++++++++++++----------- src/arch/x86/prefix/unlzma.S | 54 +++++++++++++++++- src/util/zbin.c | 52 +++++++++++++---- 3 files changed, 160 insertions(+), 45 deletions(-) diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index 425f51484..533be981e 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -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 * diff --git a/src/arch/x86/prefix/unlzma.S b/src/arch/x86/prefix/unlzma.S index 8d4b3c1a8..ce18c756f 100644 --- a/src/arch/x86/prefix/unlzma.S +++ b/src/arch/x86/prefix/unlzma.S @@ -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 diff --git a/src/util/zbin.c b/src/util/zbin.c index 1862a3827..75fba583f 100644 --- a/src/util/zbin.c +++ b/src/util/zbin.c @@ -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; }