From 3dba77a779789c3c5d3612378c7a7b14582d4a18 Mon Sep 17 00:00:00 2001 From: June Wilde Date: Mon, 26 Nov 2018 17:25:24 +0000 Subject: [PATCH] Bug 1468556 - Protect against overlapping files in libmar; r=mhowell Disallows files from referencing the same bytes in the content blocks of a MAR file by storing a list of structs containing a file's byte offsets and lengths. A list was chosen since the cap of 256 files wouldn't produce considerable overhead when extracting/reading/searching/etc through the archive. Removing the ability for a MAR file to reference the same content block repeatedly seems like a better solution than what was suggested in the BLRG report. (limiting the number of files or checking for overly large decompressed files) Allows us to prohibit this type of file bomb while only losing an attribute of the MAR file format that wasn't being leveraged. The fix is applied in mar_enum_items and mar_find_item so that the manifest the updater uses is equally safeguarded as the mar host tool. Differential Revision: https://phabricator.services.mozilla.com/D11706 --HG-- extra : moz-landing-system : lando --- modules/libmar/src/mar.h | 91 ++++++--- modules/libmar/src/mar_read.c | 190 ++++++++++++++---- .../data/manipulated_backend_collision.mar | Bin 0 -> 210 bytes .../data/manipulated_frontend_collision.mar | Bin 0 -> 210 bytes .../unit/data/manipulated_is_contained.mar | Bin 0 -> 210 bytes .../unit/data/manipulated_is_container.mar | Bin 0 -> 210 bytes .../data/manipulated_multiple_collision.mar | Bin 0 -> 249 bytes .../manipulated_multiple_collision_first.mar | Bin 0 -> 249 bytes .../manipulated_multiple_collision_last.mar | Bin 0 -> 249 bytes .../unit/data/manipulated_same_offset.mar | Bin 0 -> 210 bytes modules/libmar/tests/unit/head_libmar.js | 13 +- modules/libmar/tests/unit/test_extract.js | 72 ++++++- 12 files changed, 277 insertions(+), 89 deletions(-) create mode 100644 modules/libmar/tests/unit/data/manipulated_backend_collision.mar create mode 100644 modules/libmar/tests/unit/data/manipulated_frontend_collision.mar create mode 100644 modules/libmar/tests/unit/data/manipulated_is_contained.mar create mode 100644 modules/libmar/tests/unit/data/manipulated_is_container.mar create mode 100644 modules/libmar/tests/unit/data/manipulated_multiple_collision.mar create mode 100644 modules/libmar/tests/unit/data/manipulated_multiple_collision_first.mar create mode 100644 modules/libmar/tests/unit/data/manipulated_multiple_collision_last.mar create mode 100644 modules/libmar/tests/unit/data/manipulated_same_offset.mar diff --git a/modules/libmar/src/mar.h b/modules/libmar/src/mar.h index 58a2eed9d044..4678abd30693 100644 --- a/modules/libmar/src/mar.h +++ b/modules/libmar/src/mar.h @@ -27,28 +27,45 @@ static_assert(MAX_SIGNATURES <= 9, "too many signatures"); MOZ_STATIC_ASSERT(MAX_SIGNATURES <= 9, "too many signatures"); #endif -struct ProductInformationBlock { - const char *MARChannelID; - const char *productVersion; +struct ProductInformationBlock +{ + const char* MARChannelID; + const char* productVersion; }; /** * The MAR item data structure. */ -typedef struct MarItem_ { - struct MarItem_ *next; /* private field */ +typedef struct MarItem_ +{ + struct MarItem_* next; /* private field */ uint32_t offset; /* offset into archive */ uint32_t length; /* length of data in bytes */ uint32_t flags; /* contains file mode bits */ char name[1]; /* file path */ } MarItem; +/** + * File offset and length for tracking access of byte indexes + */ +typedef struct SeenIndex_ +{ + struct SeenIndex_* next; /* private field */ + uint32_t offset; /* offset into archive */ + uint32_t length; /* length of the data in bytes */ +} SeenIndex; + #define TABLESIZE 256 -struct MarFile_ { - FILE *fp; - MarItem *item_table[TABLESIZE]; - int item_table_is_valid; +/** + * Mozilla ARchive (MAR) file data structure + */ +struct MarFile_ +{ + FILE* fp; /* file pointer to the archive */ + MarItem* item_table[TABLESIZE]; /* hash table of files in the archive */ + SeenIndex* index_list; /* file indexes processed */ + int item_table_is_valid; /* header and index validation flag */ }; typedef struct MarFile_ MarFile; @@ -60,7 +77,7 @@ typedef struct MarFile_ MarFile; * @param data The data parameter passed by the caller of mar_enum_items. * @return A non-zero value to stop enumerating. */ -typedef int (* MarItemCallback)(MarFile *mar, const MarItem *item, void *data); +typedef int (*MarItemCallback)(MarFile* mar, const MarItem* item, void* data); /** * Open a MAR file for reading. @@ -68,7 +85,8 @@ typedef int (* MarItemCallback)(MarFile *mar, const MarItem *item, void *data); * be compatible with fopen. * @return NULL if an error occurs. */ -MarFile *mar_open(const char *path); +MarFile* +mar_open(const char* path); #ifdef XP_WIN MarFile *mar_wopen(const wchar_t *path); @@ -78,7 +96,8 @@ MarFile *mar_wopen(const wchar_t *path); * Close a MAR file that was opened using mar_open. * @param mar The MarFile object to close. */ -void mar_close(MarFile *mar); +void +mar_close(MarFile* mar); /** * Find an item in the MAR file by name. @@ -86,7 +105,8 @@ void mar_close(MarFile *mar); * @param item The name of the item to query. * @return A const reference to a MAR item or NULL if not found. */ -const MarItem *mar_find_item(MarFile *mar, const char *item); +const MarItem* +mar_find_item(MarFile* mar, const char* item); /** * Enumerate all MAR items via callback function. @@ -97,7 +117,8 @@ const MarItem *mar_find_item(MarFile *mar, const char *item); * @return 0 if the enumeration ran to completion. Otherwise, any * non-zero return value from the callback is returned. */ -int mar_enum_items(MarFile *mar, MarItemCallback callback, void *data); +int +mar_enum_items(MarFile* mar, MarItemCallback callback, void* data); /** * Read from MAR item at given offset up to bufsize bytes. @@ -109,8 +130,12 @@ int mar_enum_items(MarFile *mar, MarItemCallback callback, void *data); * @return The number of bytes written or a negative value if an * error occurs. */ -int mar_read(MarFile *mar, const MarItem *item, int offset, uint8_t *buf, - int bufsize); +int +mar_read(MarFile* mar, + const MarItem* item, + int offset, + uint8_t* buf, + int bufsize); /** * Create a MAR file from a set of files. @@ -122,10 +147,11 @@ int mar_read(MarFile *mar, const MarItem *item, int offset, uint8_t *buf, * @param infoBlock The information to store in the product information block. * @return A non-zero value if an error occurs. */ -int mar_create(const char *dest, - int numfiles, - char **files, - struct ProductInformationBlock *infoBlock); +int +mar_create(const char* dest, + int numfiles, + char** files, + struct ProductInformationBlock* infoBlock); /** * Extract a MAR file to the current working directory. @@ -133,7 +159,8 @@ int mar_create(const char *dest, * compatible with fopen. * @return A non-zero value if an error occurs. */ -int mar_extract(const char *path); +int +mar_extract(const char* path); #define MAR_MAX_CERT_SIZE (16*1024) // Way larger than necessary @@ -150,10 +177,11 @@ int mar_extract(const char *path); * * @return 0 on success, -1 on error */ -int mar_read_entire_file(const char * filePath, - uint32_t maxSize, - /*out*/ const uint8_t * *data, - /*out*/ uint32_t *size); +int +mar_read_entire_file(const char* filePath, + uint32_t maxSize, + /*out*/ const uint8_t** data, + /*out*/ uint32_t* size); /** * Verifies a MAR file by verifying each signature with the corresponding @@ -175,10 +203,11 @@ int mar_read_entire_file(const char * filePath, * a negative number if there was an error * a positive number if the signature does not verify */ -int mar_verify_signatures(MarFile *mar, - const uint8_t * const *certData, - const uint32_t *certDataSizes, - uint32_t certCount); +int +mar_verify_signatures(MarFile* mar, + const uint8_t* const* certData, + const uint32_t* certDataSizes, + uint32_t certCount); /** * Reads the product info block from the MAR file's additional block section. @@ -189,8 +218,8 @@ int mar_verify_signatures(MarFile *mar, * @return 0 on success, -1 on failure */ int -mar_read_product_info_block(MarFile *mar, - struct ProductInformationBlock *infoBlock); +mar_read_product_info_block(MarFile* mar, + struct ProductInformationBlock* infoBlock); #ifdef __cplusplus } diff --git a/modules/libmar/src/mar_read.c b/modules/libmar/src/mar_read.c index 7550170fd0eb..ddfdc0b8add4 100644 --- a/modules/libmar/src/mar_read.c +++ b/modules/libmar/src/mar_read.c @@ -19,12 +19,20 @@ sizeof(additionalBlockSize) and sizeof(additionalBlockID) */ #define MAXADDITIONALBLOCKSIZE 96 -static uint32_t mar_hash_name(const char *name) { +static uint32_t +mar_hash_name(const char* name) +{ return CityHash64(name, strlen(name)) % TABLESIZE; } -static int mar_insert_item(MarFile *mar, const char *name, int namelen, - uint32_t offset, uint32_t length, uint32_t flags) { +static int +mar_insert_item(MarFile* mar, + const char* name, + int namelen, + uint32_t offset, + uint32_t length, + uint32_t flags) +{ MarItem *item, *root; uint32_t hash; @@ -51,7 +59,9 @@ static int mar_insert_item(MarFile *mar, const char *name, int namelen, return 0; } -static int mar_consume_index(MarFile *mar, char **buf, const char *buf_end) { +static int +mar_consume_index(MarFile* mar, char** buf, const char* buf_end) +{ /* * Each item has the following structure: * uint32_t offset (network byte order) @@ -103,7 +113,9 @@ static int mar_consume_index(MarFile *mar, char **buf, const char *buf_end) { return mar_insert_item(mar, name, namelen, offset, length, flags); } -static int mar_read_index(MarFile *mar) { +static int +mar_read_index(MarFile* mar) +{ char id[MAR_ID_SIZE], *buf, *bufptr, *bufend; uint32_t offset_to_index, size_of_index; @@ -140,15 +152,73 @@ static int mar_read_index(MarFile *mar) { return (bufptr == bufend) ? 0 : -1; } +/** + * Adds an offset and length to the MarFile's index_list + * @param mar The MarFile that owns this offset length pair + * @param offset The byte offset in the archive to be marked as processed + * @param length The length corresponding to this byte offset + * @return int 1 on success, 0 if offset has been previously processed + * -1 if unable to allocate space for the SeenIndexes + */ +static int +mar_insert_offset(MarFile* mar, uint32_t offset, uint32_t length) +{ + /* Ignore files with no length */ + if (length == 0) { + return 1; + } + + SeenIndex* index = (SeenIndex*)malloc(sizeof(SeenIndex)); + if (!index) { + return -1; + } + index->next = NULL; + index->offset = offset; + index->length = length; + uint32_t index_end = index->offset + index->length - 1; + + /* If this is our first index store it at the front */ + if (mar->index_list == NULL) { + mar->index_list = index; + return 1; + } + + /* Search for matching indexes in the list of those previously visited */ + SeenIndex* previous; + SeenIndex* current = mar->index_list; + while (current != NULL) { + uint32_t current_end = current->offset + current->length - 1; + + /* If index has collided with the front or end of current or if current has + collided with the front or end of index return false */ + if ((index->offset >= current->offset && index->offset <= current_end) || + (index_end >= current->offset && index_end <= current_end) || + (current->offset >= index->offset && current->offset <= index_end) || + (current_end >= index->offset && current_end <= index_end)) { + free(index); + return 0; + } + + /* else move to the next in the list */ + previous = current; + current = current->next; + } + + /* These indexes are valid, track them */ + previous->next = index; + return 1; +} + /** * Internal shared code for mar_open and mar_wopen. * On failure, will fclose(fp). */ -static MarFile *mar_fpopen(FILE *fp) +static MarFile* +mar_fpopen(FILE* fp) { - MarFile *mar; + MarFile* mar; - mar = (MarFile *) malloc(sizeof(*mar)); + mar = (MarFile*)malloc(sizeof(*mar)); if (!mar) { fclose(fp); return NULL; @@ -157,11 +227,14 @@ static MarFile *mar_fpopen(FILE *fp) mar->fp = fp; mar->item_table_is_valid = 0; memset(mar->item_table, 0, sizeof(mar->item_table)); + mar->index_list = NULL; return mar; } -MarFile *mar_open(const char *path) { +MarFile* +mar_open(const char* path) +{ FILE *fp; fp = fopen(path, "rb"); @@ -175,7 +248,9 @@ MarFile *mar_open(const char *path) { } #ifdef XP_WIN -MarFile *mar_wopen(const wchar_t *path) { +MarFile* +mar_wopen(const wchar_t* path) +{ FILE *fp; _wfopen_s(&fp, path, L"rb"); @@ -189,8 +264,11 @@ MarFile *mar_wopen(const wchar_t *path) { } #endif -void mar_close(MarFile *mar) { - MarItem *item; +void +mar_close(MarFile* mar) +{ + MarItem* item; + SeenIndex* index; int i; fclose(mar->fp); @@ -198,12 +276,18 @@ void mar_close(MarFile *mar) { for (i = 0; i < TABLESIZE; ++i) { item = mar->item_table[i]; while (item) { - MarItem *temp = item; + MarItem* temp = item; item = item->next; free(temp); } } + while (mar->index_list != NULL) { + index = mar->index_list; + mar->index_list = index->next; + free(index); + } + free(mar); } @@ -225,12 +309,13 @@ void mar_close(MarFile *mar) { * hasAdditionalBlocks is not equal to 0. * @return 0 on success and non-zero on failure. */ -int get_mar_file_info_fp(FILE *fp, - int *hasSignatureBlock, - uint32_t *numSignatures, - int *hasAdditionalBlocks, - uint32_t *offsetAdditionalBlocks, - uint32_t *numAdditionalBlocks) +int +get_mar_file_info_fp(FILE* fp, + int* hasSignatureBlock, + uint32_t* numSignatures, + int* hasAdditionalBlocks, + uint32_t* offsetAdditionalBlocks, + uint32_t* numAdditionalBlocks) { uint32_t offsetToIndex, offsetToContent, signatureCount, signatureLen, i; @@ -363,8 +448,7 @@ int get_mar_file_info_fp(FILE *fp, * @return 0 on success, -1 on failure */ int -read_product_info_block(char *path, - struct ProductInformationBlock *infoBlock) +read_product_info_block(char* path, struct ProductInformationBlock* infoBlock) { int rv; MarFile mar; @@ -388,8 +472,8 @@ read_product_info_block(char *path, * @return 0 on success, -1 on failure */ int -mar_read_product_info_block(MarFile *mar, - struct ProductInformationBlock *infoBlock) +mar_read_product_info_block(MarFile* mar, + struct ProductInformationBlock* infoBlock) { uint32_t offsetAdditionalBlocks, numAdditionalBlocks, additionalBlockSize, additionalBlockID; @@ -476,9 +560,11 @@ mar_read_product_info_block(MarFile *mar, return -1; } -const MarItem *mar_find_item(MarFile *mar, const char *name) { +const MarItem* +mar_find_item(MarFile* mar, const char* name) +{ uint32_t hash; - const MarItem *item; + const MarItem* item; if (!mar->item_table_is_valid) { if (mar_read_index(mar)) { @@ -491,15 +577,24 @@ const MarItem *mar_find_item(MarFile *mar, const char *name) { hash = mar_hash_name(name); item = mar->item_table[hash]; - while (item && strcmp(item->name, name) != 0) + while (item && strcmp(item->name, name) != 0) { item = item->next; + } - return item; + /* If this is the first time seeing this item's indexes, return it */ + if (mar_insert_offset(mar, item->offset, item->length) == 1) { + return item; + } else { + fprintf(stderr, "ERROR: file content collision in mar_find_item()\n"); + return NULL; + } } -int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure) { - MarItem *item; - int i; +int +mar_enum_items(MarFile* mar, MarItemCallback callback, void* closure) +{ + MarItem* item; + int i, rv; if (!mar->item_table_is_valid) { if (mar_read_index(mar)) { @@ -512,9 +607,16 @@ int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure) { for (i = 0; i < TABLESIZE; ++i) { item = mar->item_table[i]; while (item) { - int rv = callback(mar, item, closure); - if (rv) - return rv; + /* if this is the first time seeing this item's indexes, process it */ + if (mar_insert_offset(mar, item->offset, item->length) == 1) { + rv = callback(mar, item, closure); + if (rv) { + return rv; + } + } else { + fprintf(stderr, "ERROR: file content collision in mar_enum_items()\n"); + return 1; + } item = item->next; } } @@ -522,8 +624,13 @@ int mar_enum_items(MarFile *mar, MarItemCallback callback, void *closure) { return 0; } -int mar_read(MarFile *mar, const MarItem *item, int offset, uint8_t *buf, - int bufsize) { +int +mar_read(MarFile* mar, + const MarItem* item, + int offset, + uint8_t* buf, + int bufsize) +{ int nr; if (offset == (int) item->length) @@ -559,12 +666,13 @@ int mar_read(MarFile *mar, const MarItem *item, int offset, uint8_t *buf, * has_additional_blocks is not equal to 0. * @return 0 on success and non-zero on failure. */ -int get_mar_file_info(const char *path, - int *hasSignatureBlock, - uint32_t *numSignatures, - int *hasAdditionalBlocks, - uint32_t *offsetAdditionalBlocks, - uint32_t *numAdditionalBlocks) +int +get_mar_file_info(const char* path, + int* hasSignatureBlock, + uint32_t* numSignatures, + int* hasAdditionalBlocks, + uint32_t* offsetAdditionalBlocks, + uint32_t* numAdditionalBlocks) { int rv; FILE *fp = fopen(path, "rb"); diff --git a/modules/libmar/tests/unit/data/manipulated_backend_collision.mar b/modules/libmar/tests/unit/data/manipulated_backend_collision.mar new file mode 100644 index 0000000000000000000000000000000000000000..41d4f78482848d279230c35db097f81bcf21bb44 GIT binary patch literal 210 zcmeZu3^HV3U|0dfAaDsnF)#w@3?OE7@O2D|clL1f^KrsQUKbf2E>g(ECO`alC;d6 P)c8aokD&oYD2V|8yq6f$ literal 0 HcmV?d00001 diff --git a/modules/libmar/tests/unit/data/manipulated_frontend_collision.mar b/modules/libmar/tests/unit/data/manipulated_frontend_collision.mar new file mode 100644 index 0000000000000000000000000000000000000000..582af58b59b13ceebc261dd0fe142471361ce241 GIT binary patch literal 210 zcmeZu3^HV3U|0dfAaDsnF)#w@3?OE7@O2D|clL1f^KrsQUKbf2E+|OECO`alC;d6 P)c8aokD(DoD2V|8yo4Cj literal 0 HcmV?d00001 diff --git a/modules/libmar/tests/unit/data/manipulated_is_contained.mar b/modules/libmar/tests/unit/data/manipulated_is_contained.mar new file mode 100644 index 0000000000000000000000000000000000000000..d51b23587d0b49f6282bcaf5f77697f1e5bad3ea GIT binary patch literal 210 zcmeZu3^HV3U|0dfAaDsnF)#w@3?OE7@O2D|clL1f^KrsQUKbf2E;8uECO`alC;d6 R)c8aokD&pGHQ+)?3;@0G80r83 literal 0 HcmV?d00001 diff --git a/modules/libmar/tests/unit/data/manipulated_is_container.mar b/modules/libmar/tests/unit/data/manipulated_is_container.mar new file mode 100644 index 0000000000000000000000000000000000000000..98b33ce9e5a895acf90a5ebf3ef9a68f7672f3bc GIT binary patch literal 210 zcmeZu3^HV3U|0dfAaDsnF)#w@3?OE7@O2D|clL1f^KrsQUKbf2E+|OtO0b^lC;d6 R)c8aokD(EWMc_h73;@3H80P>0 literal 0 HcmV?d00001 diff --git a/modules/libmar/tests/unit/data/manipulated_multiple_collision.mar b/modules/libmar/tests/unit/data/manipulated_multiple_collision.mar new file mode 100644 index 0000000000000000000000000000000000000000..7e0a3dd72458417ce0f7f38b44abd868a902883d GIT binary patch literal 249 zcmeZu3^HV3VAuo1An+4HF)#w@3?OE7@O2D|clL1f^KrsQczTIP*7G-QBYNgQ&3aT pQqWM)Q~WwAp(Kz{D~eDu0{|ksAK(B0 literal 0 HcmV?d00001 diff --git a/modules/libmar/tests/unit/data/manipulated_multiple_collision_first.mar b/modules/libmar/tests/unit/data/manipulated_multiple_collision_first.mar new file mode 100644 index 0000000000000000000000000000000000000000..a10d3eb53b30e247f9e4fae42a40f6b2c2c7b230 GIT binary patch literal 249 zcmeZu3^HV3VAuo1An+4HF)#w@3?OE7@O2D|clL1f^KrsQczTIP*7G-QBYNgQ&3aT pQqWM)Q~WwAp(K#daulIt1^^`6AOZjY literal 0 HcmV?d00001 diff --git a/modules/libmar/tests/unit/data/manipulated_multiple_collision_last.mar b/modules/libmar/tests/unit/data/manipulated_multiple_collision_last.mar new file mode 100644 index 0000000000000000000000000000000000000000..bfbb9ba8535739329d560a6d84b3ddbe5aef2ed9 GIT binary patch literal 249 zcmeZu3^HV3VAuo1An+4HF)#w@3?OE7@O2D|clL1f^KrsQczTIP*7G-QBYNgQ&3aT pQqWM)Q~Zi5p(K#d3KXGa1^^_;AOZjY literal 0 HcmV?d00001 diff --git a/modules/libmar/tests/unit/data/manipulated_same_offset.mar b/modules/libmar/tests/unit/data/manipulated_same_offset.mar new file mode 100644 index 0000000000000000000000000000000000000000..1326d1afd8d56e0311930d6e93b529ac93765a02 GIT binary patch literal 210 zcmeZu3^HV3U|0dfAaDsnF)#w@3?OE7@O2D|clL1f^KrsQUKbf2E+|OECO`alC;d6 N)c8ao4^1eE0RX&o7}5X$ literal 0 HcmV?d00001 diff --git a/modules/libmar/tests/unit/head_libmar.js b/modules/libmar/tests/unit/head_libmar.js index 2c52a65bdc6d..1ca07be7d6b1 100644 --- a/modules/libmar/tests/unit/head_libmar.js +++ b/modules/libmar/tests/unit/head_libmar.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -'use strict'; +"use strict"; const BIN_SUFFIX = mozinfo.bin_suffix; const tempDir = do_get_tempdir(); @@ -17,7 +17,7 @@ function compareBinaryData(arr1, arr2) { Assert.equal(arr1.length, arr2.length); for (let i = 0; i < arr1.length; i++) { if (arr1[i] != arr2[i]) { - throw "Data differs at index " + i + + throw "Data differs at index " + i + ", arr1: " + arr1[i] + ", arr2: " + arr2[i]; } } @@ -113,7 +113,7 @@ function createMAR(outMAR, dataDir, files) { "-V", "13.0a1", "-c", outMAR.path]; args = args.concat(files); - info('Running: ' + signmarBin.path + " " + args.join(" ")); + info("Running: " + signmarBin.path + " " + args.join(" ")); process.init(signmarBin); process.run(true, args, args.length); @@ -140,13 +140,12 @@ function extractMAR(mar, dataDir) { Assert.ok(signmarBin.exists()); Assert.ok(signmarBin.isExecutable()); - // Setup the command line arguments to create the MAR. + // Setup the command line arguments to extract the MAR. let args = ["-C", dataDir.path, "-x", mar.path]; - info('Running: ' + signmarBin.path + " " + args.join(" ")); + info("Running: " + signmarBin.path + " " + args.join(" ")); process.init(signmarBin); process.run(true, args, args.length); - // Verify signmar returned 0 for success. - Assert.equal(process.exitValue, 0); + return process.exitValue; } diff --git a/modules/libmar/tests/unit/test_extract.js b/modules/libmar/tests/unit/test_extract.js index 0218a67d6af5..9d6708a720c9 100644 --- a/modules/libmar/tests/unit/test_extract.js +++ b/modules/libmar/tests/unit/test_extract.js @@ -9,7 +9,7 @@ function run_test() { * @param marFileName The name of the MAR file to extract * @param files The files that the extracted MAR should contain */ - function run_one_test(marFileName, files) { + function extract_and_compare(marFileName, files) { // Get the MAR file that we will be extracting let mar = do_get_file("data/" + marFileName); @@ -31,8 +31,8 @@ function run_test() { refFiles.push(do_get_file("data/" + files[i])); } - // Extract the MAR contents into the ./out dir. - extractMAR(mar, outDir); + // Extract the MAR contents to ./out dir and verify 0 for success. + Assert.equal(extractMAR(mar, outDir), 0); // Compare to make sure the extracted files are the same. for (let i = 0; i < files.length; i++) { @@ -43,40 +43,92 @@ function run_test() { } } + /** + * Attempts to extract a MAR and expects a failure + * + * @param marFileName The name of the MAR file to extract + */ + function extract_and_fail(marFileName) { + // Get the MAR file that we will be extracting + let mar = do_get_file("data/" + marFileName); + + // Get the path that we will extract to + let outDir = tempDir.clone(); + outDir.append("out"); + Assert.ok(!outDir.exists()); + outDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o777); + + // Extract the MAR contents to ./out dir and verify -1 (255 from the + // nsIprocess) for failure + Assert.equal(extractMAR(mar, outDir), 1); + } + // Define the unit tests to run. let tests = { // Test extracting a MAR file with a 0 byte file. test_zero_sized: function _test_zero_sized() { - return run_one_test("0_sized.mar", ["0_sized_file"]); + return extract_and_compare("0_sized.mar", ["0_sized_file"]); }, // Test extracting a MAR file with a 1 byte file. test_one_byte: function _test_one_byte() { - return run_one_test("1_byte.mar", ["1_byte_file"]); + return extract_and_compare("1_byte.mar", ["1_byte_file"]); }, // Test extracting a MAR file with binary data. test_binary_data: function _test_binary_data() { - return run_one_test("binary_data.mar", ["binary_data_file"]); + return extract_and_compare("binary_data.mar", ["binary_data_file"]); }, // Test extracting a MAR without a product information block (PIB) which // contains binary data. test_no_pib: function _test_no_pib() { - return run_one_test("no_pib.mar", ["binary_data_file"]); + return extract_and_compare("no_pib.mar", ["binary_data_file"]); }, // Test extracting a MAR without a product information block (PIB) that is // signed and which contains binary data. test_no_pib_signed: function _test_no_pib_signed() { - return run_one_test("signed_no_pib.mar", ["binary_data_file"]); + return extract_and_compare("signed_no_pib.mar", ["binary_data_file"]); }, // Test extracting a MAR with a product information block (PIB) that is // signed and which contains binary data. test_pib_signed: function _test_pib_signed() { - return run_one_test("signed_pib.mar", ["binary_data_file"]); + return extract_and_compare("signed_pib.mar", ["binary_data_file"]); }, // Test extracting a MAR file with multiple files inside of it. test_multiple_file: function _test_multiple_file() { - return run_one_test("multiple_file.mar", + return extract_and_compare("multiple_file.mar", ["0_sized_file", "1_byte_file", "binary_data_file"]); }, + // Test collision detection where file A + B are the same offset + test_collision_same_offset: function test_collision_same_offset() { + return extract_and_fail("manipulated_same_offset.mar"); + }, + // Test collision detection where file A's indexes are a subset of file B's + test_collision_is_contained: function test_collision_is_contained() { + return extract_and_fail("manipulated_is_container.mar"); + }, + // Test collision detection where file B's indexes are a subset of file A's + test_collision_contained_by: function test_collision_contained_by() { + return extract_and_fail("manipulated_is_contained.mar"); + }, + // Test collision detection where file A ends in file B's indexes + test_collision_a_onto_b: function test_collision_a_onto_b() { + return extract_and_fail("manipulated_frontend_collision.mar"); + }, + // Test collision detection where file B ends in file A's indexes + test_collsion_b_onto_a: function test_collsion_b_onto_a() { + return extract_and_fail("manipulated_backend_collision.mar"); + }, + // Test collision detection where file C shares indexes with both file A & B + test_collision_multiple: function test_collision_multiple() { + return extract_and_fail("manipulated_multiple_collision.mar"); + }, + // Test collision detection where A is the last file in the list + test_collision_last: function test_collision_multiple_last() { + return extract_and_fail("manipulated_multiple_collision_last.mar"); + }, + // Test collision detection where A is the first file in the list + test_collision_first: function test_collision_multiple_first() { + return extract_and_fail("manipulated_multiple_collision_first.mar"); + }, // Between each test make sure the out directory and its subfiles do // not exist. cleanup_per_test: function _cleanup_per_test() {