summaryrefslogtreecommitdiffstats
path: root/bug71488.patch
blob: f4a2609d1b6e0f14dd1ecf6b98623fbb4f61e01e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
Backported from 5.5 for 5.4 by Remi Collet
binary patch dropped

From 07c7df68bd68bbe706371fccc77c814ebb335d9e Mon Sep 17 00:00:00 2001
From: Stanislav Malyshev <stas@php.net>
Date: Sun, 31 Jan 2016 19:37:56 -0800
Subject: [PATCH] Fixed bug #71488: Stack overflow when decompressing tar
 archives

---
 ext/phar/tar.c               |  22 ++++++++++++++++------
 ext/phar/tests/bug71488.phpt |  16 ++++++++++++++++
 ext/phar/tests/bug71488.tar  | Bin 0 -> 10240 bytes
 3 files changed, 32 insertions(+), 6 deletions(-)
 create mode 100644 ext/phar/tests/bug71488.phpt
 create mode 100644 ext/phar/tests/bug71488.tar

diff --git a/ext/phar/tar.c b/ext/phar/tar.c
index 5f26805..3a4bd49 100644
--- a/ext/phar/tar.c
+++ b/ext/phar/tar.c
@@ -192,6 +192,13 @@ static int phar_tar_process_metadata(pha
 }
 /* }}} */
 
+#if !HAVE_STRNLEN
+static size_t strnlen(const char *s, size_t maxlen) {
+        char *r = (char *)memchr(s, '\0', maxlen);
+        return r ? r-s : maxlen;
+}
+#endif
+
 int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, int is_data, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */
 {
 	char buf[512], *actual_alias = NULL, *p;
@@ -201,6 +208,7 @@ int phar_parse_tarfile(php_stream* fp, c
 	php_uint32 sum1, sum2, size, old;
 	phar_archive_data *myphar, **actual;
 	int last_was_longlink = 0;
+	int linkname_len;
 
 	if (error) {
 		*error = NULL;
@@ -255,7 +263,7 @@ int phar_parse_tarfile(php_stream* fp, c
 		size = entry.uncompressed_filesize = entry.compressed_filesize =
 			phar_tar_number(hdr->size, sizeof(hdr->size));
 
-		if (((!old && hdr->prefix[0] == 0) || old) && strlen(hdr->name) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
+		if (((!old && hdr->prefix[0] == 0) || old) && strnlen(hdr->name, 100) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
 			off_t curloc;
 
 			if (size > 511) {
@@ -465,20 +473,22 @@ bail:
 		}
 
 		entry.link = NULL;
-
+		/* link field is null-terminated unless it has 100 non-null chars.
+		 * Thus we can not use strlen. */
+		linkname_len = strnlen(hdr->linkname, 100);
 		if (entry.tar_type == TAR_LINK) {
-			if (!zend_hash_exists(&myphar->manifest, hdr->linkname, strlen(hdr->linkname))) {
+			if (!zend_hash_exists(&myphar->manifest, hdr->linkname, linkname_len)) {
 				if (error) {
-					spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file \"%s\"", fname, hdr->linkname);
+					spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file \"%.*s\"", fname, linkname_len, hdr->linkname);
 				}
 				pefree(entry.filename, entry.is_persistent);
 				php_stream_close(fp);
 				phar_destroy_phar_data(myphar TSRMLS_CC);
 				return FAILURE;
 			}
-			entry.link = estrdup(hdr->linkname);
+			entry.link = estrndup(hdr->linkname, linkname_len);
 		} else if (entry.tar_type == TAR_SYMLINK) {
-			entry.link = estrdup(hdr->linkname);
+			entry.link = estrndup(hdr->linkname, linkname_len);
 		}
 		phar_set_inode(&entry TSRMLS_CC);
 		zend_hash_add(&myphar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), (void **) &newentry);