From: Greg Beaver Date: Sun, 20 Apr 2008 17:28:54 +0000 (+0000) Subject: new test for bad checksum, fix tar.c to catch tar archives with a corrupted first... X-Git-Tag: RELEASE_2_0_0b1~299 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=027180d6eea7df71737b8fd5f76006b28829abf5;p=php new test for bad checksum, fix tar.c to catch tar archives with a corrupted first entry, fix memleak on bad checksum --- diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 99fe3ddac2..b429b3aab7 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1373,7 +1373,7 @@ static int phar_open_fp(php_stream* fp, char *fname, int fname_len, char *alias, return phar_open_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC); } if (got > 512) { - if (phar_is_tar(pos)) { + if (phar_is_tar(pos, fname)) { php_stream_rewind(fp); return phar_open_tarfile(fp, fname, fname_len, alias, alias_len, options, pphar, compression, error TSRMLS_CC); } diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 547df83167..b6f6bf49d6 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -413,7 +413,7 @@ int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC); int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC); /* tar functions in tar.c */ -int phar_is_tar(char *buf); +int phar_is_tar(char *buf, char *fname); int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, php_uint32 compression, char **error TSRMLS_DC); int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC); int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC); diff --git a/ext/phar/tar.c b/ext/phar/tar.c index 1aa94037fa..38ba039f53 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -97,7 +97,7 @@ static php_uint32 phar_tar_checksum(char *buf, int len) /* {{{ */ } /* }}} */ -int phar_is_tar(char *buf) +int phar_is_tar(char *buf, char *fname) { tar_header *header = (tar_header *) buf; php_uint32 checksum = phar_tar_number(header->checksum, sizeof(header->checksum)); @@ -113,6 +113,10 @@ int phar_is_tar(char *buf) memset(header->checksum, ' ', sizeof(header->checksum)); ret = (checksum == phar_tar_checksum(buf, 512)); memcpy(header->checksum, save, sizeof(header->checksum)); + if (!ret && strstr(fname, ".tar")) { + /* probably a corrupted tar - so we will pretend it is one */ + return 1; + } return ret; } @@ -196,18 +200,6 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i } memset(hdr->checksum, ' ', sizeof(hdr->checksum)); sum2 = phar_tar_checksum(buf, old?sizeof(old_tar_header):sizeof(tar_header)); - if (sum1 != sum2) { - if (error) { - spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file", fname); - } - php_stream_close(fp); - zend_hash_destroy(&myphar->manifest); - myphar->manifest.arBuckets = 0; - zend_hash_destroy(&myphar->mounted_dirs); - myphar->mounted_dirs.arBuckets = 0; - efree(myphar); - return FAILURE; - } size = entry.uncompressed_filesize = entry.compressed_filesize = phar_tar_number(hdr->size, sizeof(hdr->size)); @@ -232,6 +224,19 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i entry.filename_len--; } } + if (sum1 != sum2) { + if (error) { + spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename); + } + efree(entry.filename); + php_stream_close(fp); + zend_hash_destroy(&myphar->manifest); + myphar->manifest.arBuckets = 0; + zend_hash_destroy(&myphar->mounted_dirs); + myphar->mounted_dirs.arBuckets = 0; + efree(myphar); + return FAILURE; + } entry.tar_type = ((old & (hdr->typeflag == 0))?'0':hdr->typeflag); entry.offset = entry.offset_abs = pos; /* header_offset unused in tar */ @@ -257,6 +262,7 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i if (error) { spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - symbolic link to non-existent file", fname); } + efree(entry.filename); php_stream_close(fp); zend_hash_destroy(&myphar->manifest); myphar->manifest.arBuckets = 0; diff --git a/ext/phar/tests/tar/badchecksum.phpt b/ext/phar/tests/tar/badchecksum.phpt new file mode 100644 index 0000000000..fae19b153d --- /dev/null +++ b/ext/phar/tests/tar/badchecksum.phpt @@ -0,0 +1,30 @@ +--TEST-- +Phar: tar with bad checksum +--SKIPIF-- + +--FILE-- +init(); +$a->addFile('hithere', 'contents', null, 'checksum'); +$a->close(); + +try { + $p = new PharData($fname); +} catch (Exception $e) { + echo $e->getMessage() . "\n"; +} + +?> +===DONE=== +--CLEAN-- + +--EXPECTF-- +phar error: "%sbadchecksum.tar" is a corrupted tar file (checksum mismatch of file "hithere") +===DONE=== diff --git a/ext/phar/tests/tar/files/corrupt_tarmaker.php.inc b/ext/phar/tests/tar/files/corrupt_tarmaker.php.inc new file mode 100644 index 0000000000..28fb8a48ea --- /dev/null +++ b/ext/phar/tests/tar/files/corrupt_tarmaker.php.inc @@ -0,0 +1,170 @@ +compress = $compress; + if ($compress === 'bz2' && !function_exists('bzopen')) { + throw new PEAR2_Pyrus_Developer_Creator_Exception( + 'bzip2 extension not available'); + } + if ($compress === 'zlib' && !function_exists('gzopen')) { + throw new PEAR2_Pyrus_Developer_Creator_Exception( + 'zlib extension not available'); + } + $this->path = $path; + } + + /** + * save a file inside this package + * + * This code is modified from Vincent Lascaux's File_Archive + * package, which is licensed under the LGPL license. + * @param string relative path within the package + * @param string|resource file contents or open file handle + */ + function addFile($path, $fileOrStream, $stat = null, $corrupt = null) + { + clearstatcache(); + if ($stat === null) { + if (is_resource($fileOrStream)) { + $stat = fstat($fileOrStream); + } else { + $stat = array( + 'mode' => 0x8000 + 0644, + 'uid' => 0, + 'gid' => 0, + 'size' => strlen($fileOrStream), + 'mtime' => time(), + ); + } + } + + $link = null; + if ($stat['mode'] & 0x4000) { + $type = 5; // Directory + } else if ($stat['mode'] & 0x8000) { + $type = 0; // Regular + } else if ($stat['mode'] & 0xA000) { + $type = 1; // Link + $link = 'file1.txt'; + } else { + $type = 9; // Unknown + } + + $filePrefix = ''; + if (strlen($path) > 255) { + throw new Exception( + "$path is too long, must be 255 characters or less" + ); + } else if (strlen($path) > 100) { + $filePrefix = substr($path, 0, strlen($path)-100); + $path = substr($path, -100); + } + + $block = pack('a100a8a8a8a12A12', + $path, + decoct($stat['mode']), + sprintf('%6s ',decoct($stat['uid'])), + sprintf('%6s ',decoct($stat['gid'])), + sprintf('%11s ',decoct($stat['size'])), + sprintf('%11s ',decoct($stat['mtime'])) + ); + + $blockend = pack('a1a100a6a2a32a32a8a8a155a12', + $type, + $link, + 'ustar', + '00', + 'Pyrus', + 'Pyrus', + '', + '', + $filePrefix, + ''); + + $checkheader = array_merge(str_split($block), str_split($blockend)); + if (!function_exists('_pear2tarchecksum')) { + function _pear2tarchecksum($a, $b) {return $a + ord($b);} + } + $checksum = 256; // 8 * ord(' '); + $checksum += array_reduce($checkheader, '_pear2tarchecksum'); + + if ($corrupt == 'checksum') $checksum++; + $checksum = pack('a8', sprintf('%6s ', decoct($checksum))); + + fwrite($this->tmp, $block . $checksum . $blockend, 512); + if (is_resource($fileOrStream)) { + stream_copy_to_stream($fileOrStream, $this->tmp); + if ($stat['size'] % 512) { + fwrite($this->tmp, str_repeat("\0", 512 - $stat['size'] % 512)); + } + } else { + fwrite($this->tmp, $fileOrStream); + if (strlen($fileOrStream) % 512) { + fwrite($this->tmp, str_repeat("\0", 512 - strlen($fileOrStream) % 512)); + } + } + } + + /** + * Initialize the package creator + */ + function init() + { + switch ($this->compress) { + case 'zlib' : + $this->tmp = gzopen($this->path, 'wb'); + break; + case 'bz2' : + $this->tmp = bzopen($this->path, 'w'); + break; + case 'none' : + $this->tmp = fopen($this->path, 'wb'); + break; + default : + throw new Exception( + 'unknown compression type ' . $this->compress); + } + } + + /** + * Create an internal directory, creating parent directories as needed + * + * @param string $dir + */ + function mkdir($dir) + { + $this->addFile($dir, "", array( + 'mode' => 0x4000 + 0644, + 'uid' => 0, + 'gid' => 0, + 'size' => 0, + 'mtime' => time(), + )); + } + + /** + * Finish saving the package + */ + function close() + { + fwrite($this->tmp, pack('a1024', '')); + fclose($this->tmp); + } +} \ No newline at end of file