1
0
mirror of https://git.tukaani.org/xz.git synced 2025-10-14 13:18:18 +00:00
Lasse Collin cd69a5a6c1 BCJ filters: Reject invalid start offsets with LZMA_OPTIONS_ERROR.
This is a quick and slightly dirty fix to make the code
conform to the latest file format specification. Without
this patch, it's possible to make corrupt files by
specifying start offset that is not a multiple of the
filter's alignment. Custom start offset is almost never
used, so this was only a minor bug.

The xz command line tool doesn't validate the start offset,
so one will get a bit unclear error message if trying to use
an invalid start offset.
2009-07-10 11:39:38 +03:00

155 lines
3.4 KiB
C

///////////////////////////////////////////////////////////////////////////////
//
/// \file x86.c
/// \brief Filter for x86 binaries (BCJ filter)
///
// Authors: Igor Pavlov
// Lasse Collin
//
// This file has been put into the public domain.
// You can do whatever you want with this file.
//
///////////////////////////////////////////////////////////////////////////////
#include "simple_private.h"
#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF)
struct lzma_simple_s {
uint32_t prev_mask;
uint32_t prev_pos;
};
static size_t
x86_code(lzma_simple *simple, uint32_t now_pos, bool is_encoder,
uint8_t *buffer, size_t size)
{
static const bool MASK_TO_ALLOWED_STATUS[8]
= { true, true, true, false, true, false, false, false };
static const uint32_t MASK_TO_BIT_NUMBER[8]
= { 0, 1, 2, 2, 3, 3, 3, 3 };
uint32_t prev_mask = simple->prev_mask;
uint32_t prev_pos = simple->prev_pos;
if (size < 5)
return 0;
if (now_pos - prev_pos > 5)
prev_pos = now_pos - 5;
const size_t limit = size - 5;
size_t buffer_pos = 0;
while (buffer_pos <= limit) {
uint8_t b = buffer[buffer_pos];
if (b != 0xE8 && b != 0xE9) {
++buffer_pos;
continue;
}
const uint32_t offset = now_pos + (uint32_t)(buffer_pos)
- prev_pos;
prev_pos = now_pos + (uint32_t)(buffer_pos);
if (offset > 5) {
prev_mask = 0;
} else {
for (uint32_t i = 0; i < offset; ++i) {
prev_mask &= 0x77;
prev_mask <<= 1;
}
}
b = buffer[buffer_pos + 4];
if (Test86MSByte(b)
&& MASK_TO_ALLOWED_STATUS[(prev_mask >> 1) & 0x7]
&& (prev_mask >> 1) < 0x10) {
uint32_t src = ((uint32_t)(b) << 24)
| ((uint32_t)(buffer[buffer_pos + 3]) << 16)
| ((uint32_t)(buffer[buffer_pos + 2]) << 8)
| (buffer[buffer_pos + 1]);
uint32_t dest;
while (true) {
if (is_encoder)
dest = src + (now_pos + (uint32_t)(
buffer_pos) + 5);
else
dest = src - (now_pos + (uint32_t)(
buffer_pos) + 5);
if (prev_mask == 0)
break;
const uint32_t i = MASK_TO_BIT_NUMBER[
prev_mask >> 1];
b = (uint8_t)(dest >> (24 - i * 8));
if (!Test86MSByte(b))
break;
src = dest ^ ((1 << (32 - i * 8)) - 1);
}
buffer[buffer_pos + 4]
= (uint8_t)(~(((dest >> 24) & 1) - 1));
buffer[buffer_pos + 3] = (uint8_t)(dest >> 16);
buffer[buffer_pos + 2] = (uint8_t)(dest >> 8);
buffer[buffer_pos + 1] = (uint8_t)(dest);
buffer_pos += 5;
prev_mask = 0;
} else {
++buffer_pos;
prev_mask |= 1;
if (Test86MSByte(b))
prev_mask |= 0x10;
}
}
simple->prev_mask = prev_mask;
simple->prev_pos = prev_pos;
return buffer_pos;
}
static lzma_ret
x86_coder_init(lzma_next_coder *next, lzma_allocator *allocator,
const lzma_filter_info *filters, bool is_encoder)
{
const lzma_ret ret = lzma_simple_coder_init(next, allocator, filters,
&x86_code, sizeof(lzma_simple), 5, 1, is_encoder);
if (ret == LZMA_OK) {
next->coder->simple->prev_mask = 0;
next->coder->simple->prev_pos = (uint32_t)(-5);
}
return ret;
}
extern lzma_ret
lzma_simple_x86_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
const lzma_filter_info *filters)
{
return x86_coder_init(next, allocator, filters, true);
}
extern lzma_ret
lzma_simple_x86_decoder_init(lzma_next_coder *next, lzma_allocator *allocator,
const lzma_filter_info *filters)
{
return x86_coder_init(next, allocator, filters, false);
}