Skip to content

Fix GH-12532: PharData created from zip has incorrect timestamp #12548

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ext/phar/pharzip.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ typedef struct _phar_zip_unix3 {
/* (var.) variable symbolic link filename */
} phar_zip_unix3;

/* See https://libzip.org/specifications/extrafld.txt */
typedef struct _phar_zip_unix_time {
phar_zip_extra_field_header header;
char flags; /* flags 1 byte */
char time[4]; /* time in standard Unix format 4 bytes */
} phar_zip_unix_time;

typedef struct _phar_zip_central_dir_file {
char signature[4]; /* central file header signature 4 bytes (0x02014b50) */
char madeby[2]; /* version made by 2 bytes */
Expand Down
22 changes: 22 additions & 0 deletions ext/phar/tests/gh12532.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
GH-12532 (PharData created from zip has incorrect timestamp)
--EXTENSIONS--
phar
--FILE--
<?php

date_default_timezone_set('UTC');
$phar = new PharData(__DIR__ . '/gh12532.zip');
echo $phar->getMTime(), "\n";
echo $phar->getFileInfo()->getMTime(), "\n";
echo date('Y-m-d H:i:s', $phar->getMTime()), "\n";
echo date('Y-m-d H:i:s', $phar->getCTime()), "\n";
echo date('Y-m-d H:i:s', $phar->getATime()), "\n";

?>
--EXPECT--
1680284661
1680284661
2023-03-31 17:44:21
2023-03-31 17:44:21
2023-03-31 17:44:21
Binary file added ext/phar/tests/gh12532.zip
Binary file not shown.
30 changes: 30 additions & 0 deletions ext/phar/zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, uint16
union {
phar_zip_extra_field_header header;
phar_zip_unix3 unix3;
phar_zip_unix_time time;
} h;
size_t read;

Expand All @@ -52,6 +53,35 @@ static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, uint16
return FAILURE;
}

if (h.header.tag[0] == 'U' && h.header.tag[1] == 'T') {
/* Unix timestamp header found.
* The flags field indicates which timestamp fields are present.
* The size with a timestamp is at least 5 (== 9 - tag size) bytes, but may be larger.
* We only store the modification time in the entry, so only read that.
*/
const size_t min_size = 5;
uint16_t header_size = PHAR_GET_16(h.header.size);
if (header_size >= min_size) {
read = php_stream_read(fp, &h.time.flags, min_size);
if (read != min_size) {
return FAILURE;
}
if (h.time.flags & (1 << 0)) {
/* Modification time set */
entry->timestamp = PHAR_GET_32(h.time.time);
}

len -= header_size + 4;

/* Consume remaining bytes */
if (header_size != read) {
php_stream_seek(fp, header_size - read, SEEK_CUR);
}
continue;
}
/* Fallthrough to next if to skip header */
}

if (h.header.tag[0] != 'n' || h.header.tag[1] != 'u') {
/* skip to next header */
php_stream_seek(fp, PHAR_GET_16(h.header.size), SEEK_CUR);
Expand Down