Ed. note: documentation portions of the patch were removed. From 7deeafc02b29cc51d51079e66f4f43f986ff9cc5 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni <amadvance@gmail.com> Date: Mon, 12 Feb 2018 22:10:14 +0100 Subject: [PATCH] Fix a crash condition due invalid ZIP data --- zip.cc | 20 +++++++++++++++----- zip.h | 4 ++-- diff --git a/zip.cc b/zip.cc index b4608f7..18882ed 100644 --- a/zip.cc +++ b/zip.cc @@ -456,13 +456,15 @@ string zip_entry::name_get() const } /** Check central directory entry. */ -void zip_entry::check_cent(const unsigned char* buf) const +void zip_entry::check_cent(const unsigned char* buf, unsigned buf_size) const { + if (buf_size < ZIP_CO_FIXED) { + throw error_invalid() << "Invalid central directory data"; + } // check signature if (le_uint32_read(buf+ZIP_CO_central_file_header_signature) != ZIP_C_signature) { throw error_invalid() << "Invalid central directory signature"; } - // check filename_length > 0, can't exist a file without a name if (le_uint16_read(buf+ZIP_CO_filename_length) == 0) { throw error_invalid() << "Empty filename in central directory"; @@ -679,11 +681,11 @@ void zip_entry::save_local(FILE* f) * \param buf Fixed size cent dir. * \param f File seeked after the fixed size cent dir. */ -void zip_entry::load_cent(const unsigned char* buf, unsigned& skip) +void zip_entry::load_cent(const unsigned char* buf, unsigned buf_size, unsigned& skip) { const unsigned char* o_buf = buf; - check_cent(buf); + check_cent(buf, buf_size); // read header info.version_made_by = le_uint8_read(buf+ZIP_CO_version_made_by); @@ -705,6 +707,14 @@ void zip_entry::load_cent(const unsigned char* buf, unsigned& skip) info.relative_offset_of_local_header = le_uint32_read(buf+ZIP_CO_relative_offset_of_local_header); buf += ZIP_CO_FIXED; + if (buf_size < info.filename_length + || buf_size < info.central_extra_field_length + || buf_size < info.file_comment_length + || buf_size < ZIP_CO_FIXED + info.filename_length + info.central_extra_field_length + info.file_comment_length + ) { + throw error_invalid() << "Invalid central directory data"; + } + // read filename data_free(file_name); file_name = data_alloc(info.filename_length); @@ -853,7 +863,7 @@ void zip::open() unsigned skip = 0; try { - i->load_cent(data + data_pos, skip); + i->load_cent(data + data_pos, data_size - data_pos, skip); } catch (...) { map.erase(i); throw; diff --git a/zip.h b/zip.h index 45fdf2a..abd10c1 100644 --- a/zip.h +++ b/zip.h @@ -192,7 +192,7 @@ class zip_entry { unsigned char* central_extra_field; unsigned char* data; - void check_cent(const unsigned char* buf) const; + void check_cent(const unsigned char* buf, unsigned buf_size) const; void check_local(const unsigned char* buf) const; void check_descriptor(const unsigned char* buf) const; @@ -208,7 +208,7 @@ class zip_entry { void load_local(const unsigned char* buf, FILE* f, unsigned size); void save_local(FILE* f); - void load_cent(const unsigned char* buf, unsigned& skip); + void load_cent(const unsigned char* buf, unsigned size, unsigned& skip); void save_cent(FILE* f); void unload();