[php-src] Issue #24740: ext/phar: tar .phar/.metadata* entries have no upper bound on claimed size, unlike signature.bin/alias.txt -- unfixed sibling
of GH-22556
| From: | kunwaratulhax0r | Date: | Mon, 26 Oct 2026 13:17:53 +0000 |
| Subject: | [php-src] Issue #24740: ext/phar: tar .phar/.metadata* entries have no upper bound on claimed size, unlike signature.bin/alias.txt -- unfixed sibling of GH-22556 |
||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-253213@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24740
Author: kunwaratulhax0r
### Description
857ac7f0b3 (#24423, fixing GH-22556 "OOM DoS via oversized ././@LongLink entry in
TAR phar") added an upper bound on the size claimed by a tar @LongLink entry,
because a tar header's 12-byte octal "size" field can claim up to UINT32_MAX -
511 (~4GB) for something that should never legitimately be that large.
Phar's tar-format metadata handling has the exact same shape and was not covered by that fix.
Tar has no native "archive comment" field the way ZIP does, so phar stores its own
metadata blob in a regular tar entry under the magic filename .phar/.metadata.bin (or
.phar/.metadata/<entry>/.metadata.bin), using that entry's ordinary
file-content "size" field as the metadata length:
```c
static zend_result phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp) /* {{{ */
{
char *metadata;
size_t save = php_stream_tell(fp), read;
phar_entry_info *mentry;
metadata = (char *) safe_emalloc(1, entry->uncompressed_filesize, 1);
read = php_stream_read(fp, metadata, entry->uncompressed_filesize);
...
```
entry->uncompressed_filesize comes straight from the tar header's raw
"size" field with **no check of any kind** before this allocation -- not >
totalsize, not an absolute cap. Compare with the two other magic filenames handled in the
same parse loop, both of which already have an explicit small sanity cap before use:
- .phar/signature.bin: if (size > 511) { ...fail... }
- .phar/alias.txt: if (size > 511) { ...fail... }
.phar/.metadata* is the one magic-filename handler missing this. The allocation happens
unconditionally, before the subsequent php_stream_read() has any chance to discover the
stream doesn't actually contain that many bytes -- so the file doesn't need to contain the
claimed amount of data, just a single 512-byte header claiming it does.
For context on why the other two phar archive formats aren't affected by the same shape: ZIP
stores phar metadata in the ZIP entry's native comment field, which is inherently
16-bit-bounded by the ZIP format itself (PHAR_GET_16(zipentry.comment_len), max 65535).
The native .phar format validates its metadata length against the
already-fully-read-into-memory manifest buffer before use (ext/phar/phar.c, if
(len > (size_t)(endbuffer - buffer))). Only the tar format's convention reuses an
inherently-large file-content-size field with no additional bound.
Minimal reproducer: a hand-crafted 1,536-byte tar file (one 512-byte ustar header for an entry named
.phar/.metadata.bin with the size field set to 2147483136, followed by two all-zero
512-byte end-of-archive blocks, no actual content):
```php
<?php
$phar = new PharData('/path/to/malicious_metadata.tar');
```
Resulted in this output (memory_limit=16M):
```
Hard memory limit of 33554432 bytes exhausted at ext/phar/tar.c:215 (tried to allocate 2147483648
bytes) in ... on line 2
```
But I expected this output instead: a clean "corrupted tar file (invalid entry
size)"-style rejection, the same way the already-fixed @LongLink case and the
signature.bin/alias.txt cases reject an oversized claim, without ever
attempting the allocation.
A 1,536-byte file on disk produces a 2,147,483,648-byte allocation *attempt* -- about a
1.4-million-times amplification between crafted-file size and requested-allocation size. Confirmed
present, unmodified, on PHP-8.3, PHP-8.4, PHP-8.5, and master.
**Suggested fix:** add the same kind of bound @LongLink now has (or reuse the existing
> 511-style cap the sibling signature.bin/alias.txt
handlers already use) in phar_tar_process_metadata() before the
safe_emalloc() call.
### PHP Version
```
PHP 8.7.0-dev (cli) (built: Oct 26 2026 09:57:01) (NTS DEBUG)
Copyright © The PHP Group and Contributors
Zend Engine v4.7.0-dev, Copyright © Zend by Perforce
with Zend OPcache v8.7.0-dev, Copyright ©, by Zend by Perforce
```
Also confirmed present on the PHP-8.3, PHP-8.4, and PHP-8.5 branches via direct content check.
### Operating System
Ubuntu 24.04 (Docker), but this is pure parsing logic, not OS-specific.