From eeaec70758bfc0c0e2c0f8944c8dbeae02866206 Mon Sep 17 00:00:00 2001
From: Stanislav Malyshev <stas@php.net>
Date: Mon, 4 Aug 2014 00:01:57 -0700
Subject: [PATCH] Fix bug #67705 (extensive backtracking in rule regular
expression)
---
NEWS | 4 +++
ext/fileinfo/data_file.c | 2 +-
ext/fileinfo/libmagic/softmagic.c | 29 +++++++++++-------
ext/fileinfo/magicdata.patch | 62 +++++++++++++++++++++++++++++++++------
4 files changed, 76 insertions(+), 21 deletions(-)
diff --git a/ext/fileinfo/data_file.c b/ext/fileinfo/data_file.c
index fba4edd..15e0fa6 100644
--- a/ext/fileinfo/data_file.c
+++ b/ext/fileinfo/data_file.c
@@ -115198,7 +115198,7 @@ const unsigned char php_magic_database[2606480] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x00, 0x3D, 0x1B, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5E, 0x5C, 0x73, 0x7B, 0x30, 0x2C, 0x31, 0x30, 0x30, 0x7D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x5C,
0x73, 0x7B, 0x30, 0x2C, 0x31, 0x30, 0x30, 0x7D, 0x5B, 0x7B, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/ext/fileinfo/libmagic/softmagic.c b/ext/fileinfo/libmagic/softmagic.c
index 01e4977..7e0c856 100644
--- a/ext/fileinfo/libmagic/softmagic.c
+++ b/ext/fileinfo/libmagic/softmagic.c
@@ -58,7 +58,7 @@ private int32_t mprint(struct magic_set *, struct magic *);
private int32_t moffset(struct magic_set *, struct magic *);
private void mdebug(uint32_t, const char *, size_t);
private int mcopy(struct magic_set *, union VALUETYPE *, int, int,
- const unsigned char *, uint32_t, size_t, size_t);
+ const unsigned char *, uint32_t, size_t, struct magic *);
private int mconvert(struct magic_set *, struct magic *, int);
private int print_sep(struct magic_set *, int);
private int handle_annotation(struct magic_set *, struct magic *);
@@ -1003,7 +1003,7 @@ mdebug(uint32_t offset, const char *str, size_t len)
private int
mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir,
- const unsigned char *s, uint32_t offset, size_t nbytes, size_t linecnt)
+ const unsigned char *s, uint32_t offset, size_t nbytes, struct magic *m)
{
/*
* Note: FILE_SEARCH and FILE_REGEX do not actually copy
@@ -1023,15 +1023,24 @@ mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir,
const char *last; /* end of search region */
const char *buf; /* start of search region */
const char *end;
- size_t lines;
+ size_t lines, linecnt, bytecnt;
+ linecnt = m->str_range;
+ bytecnt = linecnt * 80;
+
+ if (bytecnt == 0) {
+ bytecnt = 8192;
+ }
+ if (bytecnt > nbytes) {
+ bytecnt = nbytes;
+ }
if (s == NULL) {
ms->search.s_len = 0;
ms->search.s = NULL;
return 0;
}
buf = RCAST(const char *, s) + offset;
- end = last = RCAST(const char *, s) + nbytes;
+ end = last = RCAST(const char *, s) + bytecnt;
/* mget() guarantees buf <= last */
for (lines = linecnt, b = buf; lines && b < end &&
((b = CAST(const char *,
@@ -1044,7 +1053,7 @@ mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir,
b++;
}
if (lines)
- last = RCAST(const char *, s) + nbytes;
+ last = RCAST(const char *, s) + bytecnt;
ms->search.s = buf;
ms->search.s_len = last - buf;
@@ -1118,7 +1127,6 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
int *need_separator, int *returnval)
{
uint32_t soffset, offset = ms->offset;
- uint32_t count = m->str_range;
int rv, oneed_separator;
char *sbuf, *rbuf;
union VALUETYPE *p = &ms->ms_value;
@@ -1130,13 +1138,12 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
}
if (mcopy(ms, p, m->type, m->flag & INDIR, s, (uint32_t)(offset + o),
- (uint32_t)nbytes, count) == -1)
+ (uint32_t)nbytes, m) == -1)
return -1;
if ((ms->flags & MAGIC_DEBUG) != 0) {
fprintf(stderr, "mget(type=%d, flag=%x, offset=%u, o=%zu, "
- "nbytes=%zu, count=%u)\n", m->type, m->flag, offset, o,
- nbytes, count);
+ "nbytes=%zu)\n", m->type, m->flag, offset, o, nbytes);
mdebug(offset, (char *)(void *)p, sizeof(union VALUETYPE));
}
@@ -1627,7 +1634,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m,
if ((ms->flags & MAGIC_DEBUG) != 0)
fprintf(stderr, "indirect +offs=%u\n", offset);
}
- if (mcopy(ms, p, m->type, 0, s, offset, nbytes, count) == -1)
+ if (mcopy(ms, p, m->type, 0, s, offset, nbytes, m) == -1)
return -1;
ms->offset = offset;
@@ -2057,7 +2064,7 @@ magiccheck(struct magic_set *ms, struct magic *m)
zval *retval;
zval *subpats;
char *haystack;
-
+
MAKE_STD_ZVAL(retval);
ALLOC_INIT_ZVAL(subpats);
--
1.9.2
From 61ec9b5b0f80bc6016548d48f433fe22e2dc24ec Mon Sep 17 00:00:00 2001
From: Stanislav Malyshev <stas@php.net>
Date: Mon, 4 Aug 2014 00:08:08 -0700
Subject: [PATCH] add test
---
ext/fileinfo/tests/cve-2014-3538.phpt | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 ext/fileinfo/tests/cve-2014-3538.phpt
diff --git a/ext/fileinfo/tests/cve-2014-3538.phpt b/ext/fileinfo/tests/cve-2014-3538.phpt
new file mode 100644
index 0000000..d6bc9c6
--- /dev/null
+++ b/ext/fileinfo/tests/cve-2014-3538.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Bug #66731: file: extensive backtraking
+--SKIPIF--
+<?php
+if (!class_exists('finfo'))
+ die('skip no fileinfo extension');
+--FILE--
+<?php
+$fd = __DIR__.'/cve-2014-3538.data';
+
+file_put_contents($fd,
+ 'try:' .
+ str_repeat("\n", 1000000));
+
+$fi = finfo_open(FILEINFO_NONE);
+$t = microtime(true);
+var_dump(finfo_file($fi, $fd));
+$t = microtime(true) - $t;
+finfo_close($fi);
+if ($t < 1) {
+ echo "Ok\n";
+} else {
+ printf("Failed, time=%.2f\n", $t);
+}
+
+?>
+Done
+--CLEAN--
+<?php
+@unlink(__DIR__.'/cve-2014-3538.data');
+?>
+--EXPECTF--
+string(%d) "%s"
+Ok
+Done
\ No newline at end of file
--
1.9.2