aboutsummaryrefslogtreecommitdiffstats
path: root/libarchive/archive_read_support_format_rar5.c
diff options
context:
space:
mode:
Diffstat (limited to 'libarchive/archive_read_support_format_rar5.c')
-rw-r--r--libarchive/archive_read_support_format_rar5.c238
1 files changed, 134 insertions, 104 deletions
diff --git a/libarchive/archive_read_support_format_rar5.c b/libarchive/archive_read_support_format_rar5.c
index ce38b1fc990f..82729bdcdb3c 100644
--- a/libarchive/archive_read_support_format_rar5.c
+++ b/libarchive/archive_read_support_format_rar5.c
@@ -73,15 +73,14 @@
* 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00
* "Rar!→•☺·\x00"
*
- * It's stored in `rar5_signature` after XOR'ing it with 0xA1, because I don't
+ * Retrieved with `rar5_signature()` by XOR'ing it with 0xA1, because I don't
* want to put this magic sequence in each binary that uses libarchive, so
* applications that scan through the file for this marker won't trigger on
* this "false" one.
*
* The array itself is decrypted in `rar5_init` function. */
-static unsigned char rar5_signature[] = { 243, 192, 211, 128, 187, 166, 160, 161 };
-static const ssize_t rar5_signature_size = sizeof(rar5_signature);
+static unsigned char rar5_signature_xor[] = { 243, 192, 211, 128, 187, 166, 160, 161 };
static const size_t g_unpack_window_size = 0x20000;
/* These could have been static const's, but they aren't, because of
@@ -211,7 +210,7 @@ struct comp_state {
or just a part of it. */
uint8_t block_parsing_finished : 1;
- int notused : 4;
+ signed int notused : 4;
int flags; /* Uncompression flags. */
int method; /* Uncompression algorithm method. */
@@ -357,6 +356,7 @@ struct rar5 {
/* Forward function declarations. */
+static void rar5_signature(char *buf);
static int verify_global_checksums(struct archive_read* a);
static int rar5_read_data_skip(struct archive_read *a);
static int push_data_ready(struct archive_read* a, struct rar5* rar,
@@ -384,7 +384,7 @@ static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) {
d->cap_mask = max_capacity_power_of_2 - 1;
d->arr = NULL;
- if((max_capacity_power_of_2 & d->cap_mask) > 0)
+ if((max_capacity_power_of_2 & d->cap_mask) != 0)
return CDE_PARAM;
cdeque_clear(d);
@@ -881,10 +881,10 @@ static inline int get_archive_read(struct archive* a,
static int read_ahead(struct archive_read* a, size_t how_many,
const uint8_t** ptr)
{
+ ssize_t avail = -1;
if(!ptr)
return 0;
- ssize_t avail = -1;
*ptr = __archive_read_ahead(a, how_many, &avail);
if(*ptr == NULL) {
return 0;
@@ -1086,11 +1086,14 @@ static int read_u64(struct archive_read* a, uint64_t* pvalue) {
static int bid_standard(struct archive_read* a) {
const uint8_t* p;
+ char signature[sizeof(rar5_signature_xor)];
+
+ rar5_signature(signature);
- if(!read_ahead(a, rar5_signature_size, &p))
+ if(!read_ahead(a, sizeof(rar5_signature_xor), &p))
return -1;
- if(!memcmp(rar5_signature, p, rar5_signature_size))
+ if(!memcmp(signature, p, sizeof(rar5_signature_xor)))
return 30;
return -1;
@@ -1150,14 +1153,14 @@ static int process_main_locator_extra_block(struct archive_read* a,
{
uint64_t locator_flags;
- if(!read_var(a, &locator_flags, NULL)) {
- return ARCHIVE_EOF;
- }
-
enum LOCATOR_FLAGS {
QLIST = 0x01, RECOVERY = 0x02,
};
+ if(!read_var(a, &locator_flags, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
if(locator_flags & QLIST) {
if(!read_var(a, &rar->qlist_offset, NULL)) {
return ARCHIVE_EOF;
@@ -1183,6 +1186,10 @@ static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,
size_t hash_type = 0;
size_t value_len;
+ enum HASH_TYPE {
+ BLAKE2sp = 0x00
+ };
+
if(!read_var_sized(a, &hash_type, &value_len))
return ARCHIVE_EOF;
@@ -1191,10 +1198,6 @@ static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,
return ARCHIVE_EOF;
}
- enum HASH_TYPE {
- BLAKE2sp = 0x00
- };
-
/* The file uses BLAKE2sp checksum algorithm instead of plain old
* CRC32. */
if(hash_type == BLAKE2sp) {
@@ -1257,6 +1260,7 @@ static int parse_file_extra_version(struct archive_read* a,
size_t value_len = 0;
struct archive_string version_string;
struct archive_string name_utf8_string;
+ const char* cur_filename;
/* Flags are ignored. */
if(!read_var_sized(a, &flags, &value_len))
@@ -1275,7 +1279,7 @@ static int parse_file_extra_version(struct archive_read* a,
/* extra_data_size should be zero here. */
- const char* cur_filename = archive_entry_pathname_utf8(e);
+ cur_filename = archive_entry_pathname_utf8(e);
if(cur_filename == NULL) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"Version entry without file name");
@@ -1586,6 +1590,25 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
char name_utf8_buf[MAX_NAME_IN_BYTES];
const uint8_t* p;
+ enum FILE_FLAGS {
+ DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,
+ UNKNOWN_UNPACKED_SIZE = 0x0008,
+ };
+
+ enum FILE_ATTRS {
+ ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4,
+ ATTR_DIRECTORY = 0x10,
+ };
+
+ enum COMP_INFO_FLAGS {
+ SOLID = 0x0040,
+ };
+
+ enum HOST_OS {
+ HOST_WINDOWS = 0,
+ HOST_UNIX = 1,
+ };
+
archive_entry_clear(entry);
/* Do not reset file context if we're switching archives. */
@@ -1615,20 +1638,6 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
return ARCHIVE_FATAL;
}
- enum FILE_FLAGS {
- DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,
- UNKNOWN_UNPACKED_SIZE = 0x0008,
- };
-
- enum FILE_ATTRS {
- ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4,
- ATTR_DIRECTORY = 0x10,
- };
-
- enum COMP_INFO_FLAGS {
- SOLID = 0x0040,
- };
-
if(!read_var_sized(a, &file_flags, NULL))
return ARCHIVE_EOF;
@@ -1725,11 +1734,6 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
if(!read_var_sized(a, &host_os, NULL))
return ARCHIVE_EOF;
- enum HOST_OS {
- HOST_WINDOWS = 0,
- HOST_UNIX = 1,
- };
-
if(host_os == HOST_WINDOWS) {
/* Host OS is Windows */
@@ -1821,12 +1825,16 @@ static int process_head_file(struct archive_read* a, struct rar5* rar,
int ret = process_head_file_extra(a, entry, rar,
extra_data_size);
- /* Sanity check. */
+ /*
+ * TODO: rewrite or remove useless sanity check
+ * as extra_data_size is not passed as a pointer
+ *
if(extra_data_size < 0) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
"File extra data size is not zero");
return ARCHIVE_FATAL;
}
+ */
if(ret != ARCHIVE_OK)
return ret;
@@ -1891,14 +1899,28 @@ static int process_head_service(struct archive_read* a, struct rar5* rar,
static int process_head_main(struct archive_read* a, struct rar5* rar,
struct archive_entry* entry, size_t block_flags)
{
- (void) entry;
-
int ret;
size_t extra_data_size = 0;
size_t extra_field_size = 0;
size_t extra_field_id = 0;
size_t archive_flags = 0;
+ enum MAIN_FLAGS {
+ VOLUME = 0x0001, /* multi-volume archive */
+ VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't
+ * have it */
+ SOLID = 0x0004, /* solid archive */
+ PROTECT = 0x0008, /* contains Recovery info */
+ LOCK = 0x0010, /* readonly flag, not used */
+ };
+
+ enum MAIN_EXTRA {
+ // Just one attribute here.
+ LOCATOR = 0x01,
+ };
+
+ (void) entry;
+
if(block_flags & HFL_EXTRA_DATA) {
if(!read_var_sized(a, &extra_data_size, NULL))
return ARCHIVE_EOF;
@@ -1910,15 +1932,6 @@ static int process_head_main(struct archive_read* a, struct rar5* rar,
return ARCHIVE_EOF;
}
- enum MAIN_FLAGS {
- VOLUME = 0x0001, /* multi-volume archive */
- VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't
- * have it */
- SOLID = 0x0004, /* solid archive */
- PROTECT = 0x0008, /* contains Recovery info */
- LOCK = 0x0010, /* readonly flag, not used */
- };
-
rar->main.volume = (archive_flags & VOLUME) > 0;
rar->main.solid = (archive_flags & SOLID) > 0;
@@ -1970,11 +1983,6 @@ static int process_head_main(struct archive_read* a, struct rar5* rar,
return ARCHIVE_FATAL;
}
- enum MAIN_EXTRA {
- // Just one attribute here.
- LOCATOR = 0x01,
- };
-
switch(extra_field_id) {
case LOCATOR:
ret = process_main_locator_extra_block(a, rar);
@@ -2080,6 +2088,8 @@ static int scan_for_signature(struct archive_read* a);
static int process_base_block(struct archive_read* a,
struct archive_entry* entry)
{
+ const size_t SMALLEST_RAR5_BLOCK_SIZE = 3;
+
struct rar5* rar = get_context(a);
uint32_t hdr_crc, computed_crc;
size_t raw_hdr_size = 0, hdr_size_len, hdr_size;
@@ -2088,6 +2098,12 @@ static int process_base_block(struct archive_read* a,
const uint8_t* p;
int ret;
+ enum HEADER_TYPE {
+ HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02,
+ HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,
+ HEAD_UNKNOWN = 0xff,
+ };
+
/* Skip any unprocessed data for this file. */
ret = skip_unprocessed_bytes(a);
if(ret != ARCHIVE_OK)
@@ -2103,15 +2119,26 @@ static int process_base_block(struct archive_read* a,
return ARCHIVE_EOF;
}
+ hdr_size = raw_hdr_size + hdr_size_len;
+
/* Sanity check, maximum header size for RAR5 is 2MB. */
- if(raw_hdr_size > (2 * 1024 * 1024)) {
+ if(hdr_size > (2 * 1024 * 1024)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
"Base block header is too large");
return ARCHIVE_FATAL;
}
- hdr_size = raw_hdr_size + hdr_size_len;
+ /* Additional sanity checks to weed out invalid files. */
+ if(raw_hdr_size == 0 || hdr_size_len == 0 ||
+ hdr_size < SMALLEST_RAR5_BLOCK_SIZE)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Too small block encountered (%zu bytes)",
+ raw_hdr_size);
+
+ return ARCHIVE_FATAL;
+ }
/* Read the whole header data into memory, maximum memory use here is
* 2MB. */
@@ -2146,12 +2173,6 @@ static int process_base_block(struct archive_read* a,
rar->main.endarc = 0;
/* Those are possible header ids in RARv5. */
- enum HEADER_TYPE {
- HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02,
- HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,
- HEAD_UNKNOWN = 0xff,
- };
-
switch(header_id) {
case HEAD_MAIN:
ret = process_head_main(a, rar, entry, header_flags);
@@ -2264,7 +2285,7 @@ static int rar5_read_header(struct archive_read *a,
}
if(rar->skipped_magic == 0) {
- if(ARCHIVE_OK != consume(a, rar5_signature_size)) {
+ if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) {
return ARCHIVE_EOF;
}
@@ -2536,12 +2557,10 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
/* 0..15: store directly */
table[i] = (uint8_t) num;
i++;
- continue;
- }
-
- if(num < 18) {
+ } else if(num < 18) {
/* 16..17: repeat previous code */
uint16_t n;
+
if(ARCHIVE_OK != read_bits_16(rar, p, &n))
return ARCHIVE_EOF;
@@ -2567,27 +2586,26 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
"huffman tables");
return ARCHIVE_FATAL;
}
+ } else {
+ /* other codes: fill with zeroes `n` times */
+ uint16_t n;
- continue;
- }
+ if(ARCHIVE_OK != read_bits_16(rar, p, &n))
+ return ARCHIVE_EOF;
- /* other codes: fill with zeroes `n` times */
- uint16_t n;
- if(ARCHIVE_OK != read_bits_16(rar, p, &n))
- return ARCHIVE_EOF;
+ if(num == 18) {
+ n >>= 13;
+ n += 3;
+ skip_bits(rar, 3);
+ } else {
+ n >>= 9;
+ n += 11;
+ skip_bits(rar, 7);
+ }
- if(num == 18) {
- n >>= 13;
- n += 3;
- skip_bits(rar, 3);
- } else {
- n >>= 9;
- n += 11;
- skip_bits(rar, 7);
+ while(n-- > 0 && i < HUFF_TABLE_SIZE)
+ table[i++] = 0;
}
-
- while(n-- > 0 && i < HUFF_TABLE_SIZE)
- table[i++] = 0;
}
ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC);
@@ -2632,6 +2650,7 @@ static int parse_tables(struct archive_read* a, struct rar5* rar,
static int parse_block_header(struct archive_read* a, const uint8_t* p,
ssize_t* block_size, struct compressed_block_header* hdr)
{
+ uint8_t calculated_cksum;
memcpy(hdr, p, sizeof(struct compressed_block_header));
if(bf_byte_count(hdr) > 2) {
@@ -2670,7 +2689,7 @@ static int parse_block_header(struct archive_read* a, const uint8_t* p,
/* Verify the block header checksum. 0x5A is a magic value and is
* always * constant. */
- uint8_t calculated_cksum = 0x5A
+ calculated_cksum = 0x5A
^ (uint8_t) hdr->block_flags_u8
^ (uint8_t) *block_size
^ (uint8_t) (*block_size >> 8)
@@ -2744,6 +2763,7 @@ static int is_valid_filter_block_start(struct rar5* rar,
static int parse_filter(struct archive_read* ar, const uint8_t* p) {
uint32_t block_start, block_length;
uint16_t filter_type;
+ struct filter_info* filt = NULL;
struct rar5* rar = get_context(ar);
/* Read the parameters from the input stream. */
@@ -2774,7 +2794,7 @@ static int parse_filter(struct archive_read* ar, const uint8_t* p) {
}
/* Allocate a new filter. */
- struct filter_info* filt = add_new_filter(rar);
+ filt = add_new_filter(rar);
if(filt == NULL) {
archive_set_error(&ar->archive, ENOMEM,
"Can't allocate memory for a filter descriptor.");
@@ -3043,7 +3063,8 @@ static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
}
continue;
- } else if(num < 262) {
+ } else {
+ /* num < 262 */
const int idx = num - 258;
const int dist = dist_cache_touch(rar, idx);
@@ -3079,6 +3100,7 @@ static int scan_for_signature(struct archive_read* a) {
const uint8_t* p;
const int chunk_size = 512;
ssize_t i;
+ char signature[sizeof(rar5_signature_xor)];
/* If we're here, it means we're on an 'unknown territory' data.
* There's no indication what kind of data we're reading here.
@@ -3092,19 +3114,23 @@ static int scan_for_signature(struct archive_read* a) {
* end of the file? If so, it would be a better approach than the
* current implementation of this function. */
+ rar5_signature(signature);
+
while(1) {
if(!read_ahead(a, chunk_size, &p))
return ARCHIVE_EOF;
- for(i = 0; i < chunk_size - rar5_signature_size; i++) {
- if(memcmp(&p[i], rar5_signature,
- rar5_signature_size) == 0) {
+ for(i = 0; i < chunk_size - (int)sizeof(rar5_signature_xor);
+ i++) {
+ if(memcmp(&p[i], signature,
+ sizeof(rar5_signature_xor)) == 0) {
/* Consume the number of bytes we've used to
* search for the signature, as well as the
* number of bytes used by the signature
* itself. After this we should be standing
* on a valid base block header. */
- (void) consume(a, i + rar5_signature_size);
+ (void) consume(a,
+ i + sizeof(rar5_signature_xor));
return ARCHIVE_OK;
}
}
@@ -3314,6 +3340,8 @@ static int process_block(struct archive_read* a) {
if(rar->cstate.block_parsing_finished) {
ssize_t block_size;
+ ssize_t to_skip;
+ ssize_t cur_block_size;
/* The header size won't be bigger than 6 bytes. */
if(!read_ahead(a, 6, &p)) {
@@ -3337,7 +3365,7 @@ static int process_block(struct archive_read* a) {
/* Skip block header. Next data is huffman tables,
* if present. */
- ssize_t to_skip = sizeof(struct compressed_block_header) +
+ to_skip = sizeof(struct compressed_block_header) +
bf_byte_count(&rar->last_block_hdr) + 1;
if(ARCHIVE_OK != consume(a, to_skip))
@@ -3351,7 +3379,7 @@ static int process_block(struct archive_read* a) {
* bigger than the actual data stored in this file. Remaining
* part of the data will be in another file. */
- ssize_t cur_block_size =
+ cur_block_size =
rar5_min(rar->file.bytes_remaining, block_size);
if(block_size > rar->file.bytes_remaining) {
@@ -3679,6 +3707,7 @@ static int uncompress_file(struct archive_read* a) {
static int do_unstore_file(struct archive_read* a,
struct rar5* rar, const void** buf, size_t* size, int64_t* offset)
{
+ size_t to_read;
const uint8_t* p;
if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 &&
@@ -3697,7 +3726,7 @@ static int do_unstore_file(struct archive_read* a,
}
}
- size_t to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);
+ to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);
if(to_read == 0) {
return ARCHIVE_EOF;
}
@@ -3866,6 +3895,18 @@ static int verify_global_checksums(struct archive_read* a) {
return verify_checksums(a);
}
+/*
+ * Decryption function for the magic signature pattern. Check the comment near
+ * the `rar5_signature_xor` symbol to read the rationale behind this.
+ */
+static void rar5_signature(char *buf) {
+ size_t i;
+
+ for(i = 0; i < sizeof(rar5_signature_xor); i++) {
+ buf[i] = rar5_signature_xor[i] ^ 0xA1;
+ }
+}
+
static int rar5_read_data(struct archive_read *a, const void **buff,
size_t *size, int64_t *offset) {
int ret;
@@ -4012,19 +4053,8 @@ static int rar5_has_encrypted_entries(struct archive_read *_a) {
}
static int rar5_init(struct rar5* rar) {
- ssize_t i;
-
memset(rar, 0, sizeof(struct rar5));
- /* Decrypt the magic signature pattern. Check the comment near the
- * `rar5_signature` symbol to read the rationale behind this. */
-
- if(rar5_signature[0] == 243) {
- for(i = 0; i < rar5_signature_size; i++) {
- rar5_signature[i] ^= 0xA1;
- }
- }
-
if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192))
return ARCHIVE_FATAL;