From 82dc03941a315c24faaecefa95d3c2add47115a5 Mon Sep 17 00:00:00 2001 From: Greg Beaver Date: Mon, 12 May 2008 20:42:07 +0000 Subject: [PATCH] fix weird alias issues, add tests to check for new-found problems --- ext/phar/phar.c | 55 ++++++++++------ ext/phar/phar_object.c | 3 + ext/phar/tar.c | 25 +++++-- ext/phar/tests/alias_acrobatics.phpt | 46 +++++++++++++ ext/phar/tests/phar_setalias2.phpt | 2 +- ext/phar/tests/tar/alias_acrobatics.phpt | 46 +++++++++++++ ext/phar/tests/zip/033.phpt | 1 + ext/phar/tests/zip/alias_acrobatics.phpt | 46 +++++++++++++ ext/phar/tests/zip/badalias.phpt | 10 +-- ext/phar/util.c | 6 ++ ext/phar/zip.c | 83 +++++++++++++++--------- 11 files changed, 262 insertions(+), 61 deletions(-) create mode 100644 ext/phar/tests/alias_acrobatics.phpt create mode 100644 ext/phar/tests/tar/alias_acrobatics.phpt create mode 100644 ext/phar/tests/zip/alias_acrobatics.phpt diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 560a9f333b..ab2cb08ac4 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -420,10 +420,8 @@ int phar_open_loaded(char *fname, int fname_len, char *alias, int alias_len, int if (pphar) { *pphar = NULL; } - if (phar && alias && (options & REPORT_ERRORS)) { - if (error) { - spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, phar->fname, fname); - } + if (phar && error && !(options & REPORT_ERRORS)) { + efree(error); } return FAILURE; } @@ -984,6 +982,7 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */ { const char *ext_str, *z; + char *my_error; int ext_len; phar_archive_data **test, *unused = NULL; @@ -1005,7 +1004,7 @@ int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int al } check_file: - if (phar_open_loaded(fname, fname_len, alias, alias_len, is_data, options, test, 0 TSRMLS_CC) == SUCCESS) { + if (phar_open_loaded(fname, fname_len, alias, alias_len, is_data, options, test, &my_error TSRMLS_CC) == SUCCESS) { if (pphar) { *pphar = *test; } @@ -1026,6 +1025,13 @@ check_file: (*test)->is_writeable = 1; } return SUCCESS; + } else if (my_error) { + if (error) { + *error = my_error; + } else { + efree(my_error); + } + return FAILURE; } if (ext_len > 3 && (z = memchr(ext_str, 'z', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ip", 2)) { @@ -1045,7 +1051,6 @@ check_file: int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */ { phar_archive_data *mydata; - int register_alias; php_stream *fp; char *actual = NULL, *p; @@ -1126,36 +1131,46 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a zend_hash_init(&mydata->mounted_dirs, sizeof(char *), zend_get_hash_value, NULL, 0); mydata->fname_len = fname_len; - if (is_data) { - alias = NULL; - alias_len = 0; - } else { - mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len); - mydata->alias_len = alias ? alias_len : fname_len; - } snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION); mydata->is_temporary_alias = alias ? 0 : 1; mydata->internal_file_start = -1; mydata->fp = NULL; mydata->is_writeable = 1; mydata->is_brandnew = 1; - if (!alias_len || !alias) { - /* if we neither have an explicit nor an implicit alias, we use the filename */ + phar_request_initialize(TSRMLS_C); + zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); + if (is_data) { alias = NULL; alias_len = 0; - register_alias = 0; } else { - register_alias = 1; + phar_archive_data **fd_ptr; + + if (alias && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias); + } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); + if (pphar) { + *pphar = NULL; + } + return FAILURE; + } + } + mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(mydata->fname, fname_len); + mydata->alias_len = alias ? alias_len : fname_len; } - phar_request_initialize(TSRMLS_C); - zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); - if (register_alias) { + if (alias_len && alias) { if (FAILURE == zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL)) { if (options & REPORT_ERRORS) { if (error) { spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", fname, alias); } } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); + if (pphar) { + *pphar = NULL; + } return FAILURE; } } diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 3b5ae23e0d..98b602adbc 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -1157,6 +1157,9 @@ PHP_METHOD(Phar, __construct) zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "%s", error); efree(error); + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, + "Phar creation or opening failed"); } return; } diff --git a/ext/phar/tar.c b/ext/phar/tar.c index a97bbf0125..ac19ef3f68 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -400,8 +400,8 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i phar_archive_data **fd_ptr; myphar->is_temporary_alias = 0; - if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { - if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, myphar->alias_len TSRMLS_CC)) { if (error) { spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname); } @@ -411,8 +411,25 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i } zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); } else { - myphar->alias = estrndup(myphar->fname, fname_len); - myphar->alias_len = fname_len; + phar_archive_data **fd_ptr; + + if (alias_len) { + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname); + } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), myphar->fname, fname_len); + return FAILURE; + } + } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, myphar->alias_len, (void*)&myphar, sizeof(phar_archive_data*), NULL); + myphar->alias = estrndup(alias, alias_len); + myphar->alias_len = alias_len; + } else { + myphar->alias = estrndup(myphar->fname, fname_len); + myphar->alias_len = fname_len; + } myphar->is_temporary_alias = 1; } if (pphar) { diff --git a/ext/phar/tests/alias_acrobatics.phpt b/ext/phar/tests/alias_acrobatics.phpt new file mode 100644 index 0000000000..1bb20a4b3a --- /dev/null +++ b/ext/phar/tests/alias_acrobatics.phpt @@ -0,0 +1,46 @@ +--TEST-- +Phar: alias edge cases +--SKIPIF-- + +--INI-- +phar.readonly=0 +--FILE-- +setAlias('foo'); +$p['unused'] = 'hi'; +try { +$a = new Phar($fname2, 0, 'foo'); +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +copy($fname, $fname2); +echo "2\n"; +try { +$a = new Phar($fname2); +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +try { +$b = new Phar($fname, 0, 'another'); +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +?> +===DONE=== +--CLEAN-- + +--EXPECTF-- +alias "foo" is already used for archive "%salias_acrobatics.phar" cannot be overloaded with "%salias_acrobatics.2.phar" +2 +Cannot open archive "%salias_acrobatics.2.phar", alias is already in use by existing archive +alias "another" is already used for archive "%salias_acrobatics.phar" cannot be overloaded with "%salias_acrobatics.phar" +===DONE=== diff --git a/ext/phar/tests/phar_setalias2.phpt b/ext/phar/tests/phar_setalias2.phpt index df8f42282e..901e4d2ebd 100644 --- a/ext/phar/tests/phar_setalias2.phpt +++ b/ext/phar/tests/phar_setalias2.phpt @@ -47,5 +47,5 @@ __HALT_COMPILER(); hio test alias "test" is already used for archive "%sphar_setalias2.phar.php" and cannot be used for other archives -archive "%snope.phar" cannot be associated with alias "test", already in use +alias "test" is already used for archive "%sphar_setalias2.phar.php" cannot be overloaded with "%snope.phar" ===DONE=== diff --git a/ext/phar/tests/tar/alias_acrobatics.phpt b/ext/phar/tests/tar/alias_acrobatics.phpt new file mode 100644 index 0000000000..d08dc0467c --- /dev/null +++ b/ext/phar/tests/tar/alias_acrobatics.phpt @@ -0,0 +1,46 @@ +--TEST-- +Phar: alias edge cases +--SKIPIF-- + +--INI-- +phar.readonly=0 +--FILE-- +setAlias('foo'); +$p['unused'] = 'hi'; +try { +$a = new Phar($fname2, 0, 'foo'); +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +copy($fname, $fname2); +echo "2\n"; +try { +$a = new Phar($fname2); +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +try { +$b = new Phar($fname, 0, 'another'); +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +?> +===DONE=== +--CLEAN-- + +--EXPECTF-- +alias "foo" is already used for archive "%salias_acrobatics.phar.tar" cannot be overloaded with "%salias_acrobatics.2.phar.tar" +2 +phar error: Unable to add tar-based phar "%salias_acrobatics.2.phar.tar", alias is already in use +alias "another" is already used for archive "%salias_acrobatics.phar.tar" cannot be overloaded with "%salias_acrobatics.phar.tar" +===DONE=== diff --git a/ext/phar/tests/zip/033.phpt b/ext/phar/tests/zip/033.phpt index 3c1e2de7bc..c980758a83 100644 --- a/ext/phar/tests/zip/033.phpt +++ b/ext/phar/tests/zip/033.phpt @@ -22,6 +22,7 @@ try { var_dump($phar['a.php']->isExecutable()); $phar['a.php']->chmod(0777); copy($fname, $fname2); + $phar->setAlias('unused'); $phar2 = new Phar($fname2); var_dump($phar2['a.php']->isExecutable()); $phar['a.php']->chmod(0666); diff --git a/ext/phar/tests/zip/alias_acrobatics.phpt b/ext/phar/tests/zip/alias_acrobatics.phpt new file mode 100644 index 0000000000..2a58e39efc --- /dev/null +++ b/ext/phar/tests/zip/alias_acrobatics.phpt @@ -0,0 +1,46 @@ +--TEST-- +Phar: alias edge cases +--SKIPIF-- + +--INI-- +phar.readonly=0 +--FILE-- +setAlias('foo'); +$p['unused'] = 'hi'; +try { +$a = new Phar($fname2, 0, 'foo'); +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +copy($fname, $fname2); +echo "2\n"; +try { +$a = new Phar($fname2); +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +try { +$b = new Phar($fname, 0, 'another'); +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +?> +===DONE=== +--CLEAN-- + +--EXPECTF-- +alias "foo" is already used for archive "%salias_acrobatics.phar.zip" cannot be overloaded with "%salias_acrobatics.2.phar.zip" +2 +phar error: Unable to add zip-based phar "%salias_acrobatics.2.phar.zip" with implicit alias, alias is already in use +alias "another" is already used for archive "%salias_acrobatics.phar.zip" cannot be overloaded with "%salias_acrobatics.phar.zip" +===DONE=== diff --git a/ext/phar/tests/zip/badalias.phpt b/ext/phar/tests/zip/badalias.phpt index 4e08cefd5e..0291c4b089 100644 --- a/ext/phar/tests/zip/badalias.phpt +++ b/ext/phar/tests/zip/badalias.phpt @@ -17,9 +17,9 @@ echo $ee->getMessage(), "\n"; ?> ===DONE=== --EXPECTF-- -phar error: invalid alias in zip-based phar "%sbadalias1.phar.zip" -phar error: invalid alias in zip-based phar "%sbadalias2.phar.zip" -phar error: invalid alias in zip-based phar "%sbadalias3.phar.zip" -phar error: invalid alias in zip-based phar "%sbadalias4.phar.zip" -phar error: invalid alias in zip-based phar "%sbadalias5.phar.zip" +phar error: invalid alias "hi/there" in zip-based phar "%sbadalias1.phar.zip" +phar error: invalid alias "hi\there" in zip-based phar "%sbadalias2.phar.zip" +phar error: invalid alias "hi\there" in zip-based phar "%sbadalias3.phar.zip" +phar error: invalid alias "hi;there" in zip-based phar "%sbadalias4.phar.zip" +phar error: invalid alias "hi:there" in zip-based phar "%sbadalias5.phar.zip" ===DONE=== diff --git a/ext/phar/util.c b/ext/phar/util.c index 5bdd018fdf..77e83098a4 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -1032,6 +1032,12 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch *archive = *fd_ptr; fd = *fd_ptr; if (alias && alias_len) { + if (!fd->is_temporary_alias && (alias_len != fd->alias_len || memcmp(fd->alias, alias, alias_len))) { + if (error) { + spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, (*fd_ptr)->fname, fname); + } + return FAILURE; + } if (fd->alias_len && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), fd->alias, fd->alias_len, (void**)&fd_ptr)) { zend_hash_del(&(PHAR_GLOBALS->phar_alias_map), fd->alias, fd->alias_len); } diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 4b20690467..0261033ff9 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -152,7 +152,7 @@ int phar_open_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, i php_uint16 i; phar_archive_data *mydata = NULL; phar_entry_info entry = {0}; - char *p = buf, *ext; + char *p = buf, *ext, *actual_alias = NULL; size = php_stream_tell(fp); if (size > sizeof(locator) + 65536) { @@ -241,13 +241,6 @@ foundit: mydata->ext_len = (mydata->fname + fname_len) - mydata->ext; } } - if (!alias_len) { - mydata->alias = estrndup(fname, fname_len); -#ifdef PHP_WIN32 - phar_unixify_path_separators(mydata->alias, fname_len); -#endif - mydata->alias_len = fname_len; - } /* clean up on big-endian systems */ /* seek to central directory */ php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET); @@ -400,15 +393,15 @@ foundit: } else { entry.metadata = NULL; } - if (entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { + if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { php_stream_filter *filter; off_t saveloc; - phar_archive_data **fd_ptr; /* archive alias found, seek to file contents, do not validate local header. Potentially risky, but not very. */ saveloc = php_stream_tell(fp); php_stream_seek(fp, PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET); + mydata->alias_len = entry.uncompressed_filesize; if (entry.flags & PHAR_ENT_COMPRESSED_GZ) { filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp) TSRMLS_CC); if (!filter) { @@ -416,9 +409,7 @@ foundit: PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed"); } php_stream_filter_append(&fp->readfilters, filter); - efree(mydata->alias); - mydata->alias = NULL; - if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) { + if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) { efree(entry.filename); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } @@ -433,35 +424,19 @@ foundit: } php_stream_filter_append(&fp->readfilters, filter); php_stream_filter_append(&fp->readfilters, filter); - efree(mydata->alias); - mydata->alias = NULL; - if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) { + if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) { efree(entry.filename); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } php_stream_filter_flush(filter, 1); php_stream_filter_remove(filter, 1 TSRMLS_CC); } else { - efree(mydata->alias); - mydata->alias = NULL; - if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &(mydata->alias), entry.uncompressed_filesize, 0)) || !mydata->alias) { + if (!(entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)) || !actual_alias) { efree(entry.filename); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } } - mydata->is_temporary_alias = 0; - mydata->alias_len = PHAR_GET_32(zipentry.uncompsize); - if (!phar_validate_alias(mydata->alias, mydata->alias_len)) { - efree(entry.filename); - PHAR_ZIP_FAIL("invalid alias"); - } - if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { - if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { - PHAR_ZIP_FAIL("alias is already in use by existing archive"); - } - } - zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), mydata->alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); /* return to central directory parsing */ php_stream_seek(fp, saveloc, SEEK_SET); } @@ -469,6 +444,52 @@ foundit: } mydata->fp = fp; zend_hash_add(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); + if (actual_alias) { + phar_archive_data **fd_ptr; + + if (!phar_validate_alias(actual_alias, mydata->alias_len)) { + if (error) { + spprintf(error, 4096, "phar error: invalid alias \"%s\" in zip-based phar \"%s\"", actual_alias, fname); + } + efree(actual_alias); + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); + return FAILURE; + } + mydata->is_temporary_alias = 0; + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, actual_alias, mydata->alias_len TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with implicit alias, alias is already in use", fname); + } + efree(actual_alias); + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); + return FAILURE; + } + } + mydata->alias = actual_alias; + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); + } else { + phar_archive_data **fd_ptr; + + if (alias_len) { + if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_alias_map), alias, alias_len, (void **)&fd_ptr)) { + if (SUCCESS != phar_free_alias(*fd_ptr, alias, alias_len TSRMLS_CC)) { + if (error) { + spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with explicit alias, alias is already in use", fname); + } + zend_hash_del(&(PHAR_GLOBALS->phar_fname_map), mydata->fname, fname_len); + return FAILURE; + } + } + zend_hash_add(&(PHAR_GLOBALS->phar_alias_map), actual_alias, mydata->alias_len, (void*)&mydata, sizeof(phar_archive_data*), NULL); + mydata->alias = estrndup(alias, alias_len); + mydata->alias_len = alias_len; + } else { + mydata->alias = estrndup(mydata->fname, fname_len); + mydata->alias_len = fname_len; + } + mydata->is_temporary_alias = 1; + } if (pphar) { *pphar = mydata; } -- 2.40.0