New version 8.0.24

This commit is contained in:
Funda Wang 2022-10-09 13:15:05 +08:00
parent 0a79d4d49e
commit e057fe19c4
15 changed files with 86 additions and 1302 deletions

View File

@ -1,211 +0,0 @@
From 4a89e726bd4d0571991dc22a9a1ad4509e8fe347 Mon Sep 17 00:00:00 2001
From: "Christoph M. Becker" <cmbecker69@gmx.de>
Date: Tue, 19 Jan 2021 11:23:25 +0100
Subject: [PATCH] Alternative fix for bug 77423
That bug report originally was about `parse_url()` misbehaving, but the
security aspect was actually only regarding `FILTER_VALIDATE_URL`.
Since the changes to `parse_url_ex()` apparently affect userland code
which is relying on the sloppy URL parsing[1], this alternative
restores the old parsing behavior, but ensures that the userinfo is
checked for correctness for `FILTER_VALIDATE_URL`.
[1] <https://github.com/php/php-src/commit/5174de7cd33c3d4fa591c9c93859ff9989b07e8c#commitcomment-45967652>
---
ext/filter/logical_filters.c | 23 +++++++++++++++++++
.../tests/url => filter/tests}/bug77423.phpt | 15 ------------
ext/standard/tests/strings/url_t.phpt | 6 +++--
.../tests/url/parse_url_basic_001.phpt | 6 +++--
.../tests/url/parse_url_basic_003.phpt | 2 +-
.../tests/url/parse_url_basic_005.phpt | 2 +-
.../tests/url/parse_url_unterminated.phpt | 6 +++--
ext/standard/url.c | 6 +----
8 files changed, 38 insertions(+), 28 deletions(-)
rename ext/{standard/tests/url => filter/tests}/bug77423.phpt (53%)
diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c
index 076d2479461f..1cf345dbb58a 100644
--- a/ext/filter/logical_filters.c
+++ b/ext/filter/logical_filters.c
@@ -532,6 +532,22 @@ void php_filter_validate_domain(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
}
/* }}} */
+static int is_userinfo_valid(zend_string *str)
+{
+ const char *valid = "-._~!$&'()*+,;=:";
+ const char *p = ZSTR_VAL(str);
+ while (p - ZSTR_VAL(str) < ZSTR_LEN(str)) {
+ if (isalpha(*p) || isdigit(*p) || strchr(valid, *p)) {
+ p++;
+ } else if (*p == '%' && p - ZSTR_VAL(str) <= ZSTR_LEN(str) - 3 && isdigit(*(p+1)) && isxdigit(*(p+2))) {
+ p += 3;
+ } else {
+ return 0;
+ }
+ }
+ return 1;
+}
+
void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
{
php_url *url;
@@ -592,6 +608,13 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
php_url_free(url);
RETURN_VALIDATION_FAILED
}
+
+ if (url->user != NULL && !is_userinfo_valid(url->user)) {
+ php_url_free(url);
+ RETURN_VALIDATION_FAILED
+
+ }
+
php_url_free(url);
}
/* }}} */
diff --git a/ext/standard/tests/url/bug77423.phpt b/ext/filter/tests/bug77423.phpt
similarity index 53%
rename from ext/standard/tests/url/bug77423.phpt
rename to ext/filter/tests/bug77423.phpt
index be03fe95e24e..761c7c359a92 100644
--- a/ext/standard/tests/url/bug77423.phpt
+++ b/ext/filter/tests/bug77423.phpt
@@ -8,23 +8,8 @@ $urls = array(
);
foreach ($urls as $url) {
var_dump(filter_var($url, FILTER_VALIDATE_URL));
- var_dump(parse_url($url));
}
?>
--EXPECT--
bool(false)
-array(3) {
- ["scheme"]=>
- string(4) "http"
- ["host"]=>
- string(19) "php.net\@aliyun.com"
- ["path"]=>
- string(7) "/aaa.do"
-}
bool(false)
-array(2) {
- ["scheme"]=>
- string(5) "https"
- ["host"]=>
- string(26) "example.com\uFF03@bing.com"
-}
diff --git a/ext/standard/tests/strings/url_t.phpt b/ext/standard/tests/strings/url_t.phpt
index f564f59f0632..79ff3bc4a8e3 100644
--- a/ext/standard/tests/strings/url_t.phpt
+++ b/ext/standard/tests/strings/url_t.phpt
@@ -575,13 +575,15 @@ $sample_urls = array (
string(16) "some_page_ref123"
}
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(6) {
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
["scheme"]=>
string(4) "http"
["host"]=>
- string(26) "secret@hideout@www.php.net"
+ string(11) "www.php.net"
["port"]=>
int(80)
+ ["user"]=>
+ string(14) "secret@hideout"
["path"]=>
string(10) "/index.php"
["query"]=>
diff --git a/ext/standard/tests/url/parse_url_basic_001.phpt b/ext/standard/tests/url/parse_url_basic_001.phpt
index 57014f08391a..7ffd001856c8 100644
--- a/ext/standard/tests/url/parse_url_basic_001.phpt
+++ b/ext/standard/tests/url/parse_url_basic_001.phpt
@@ -506,13 +506,15 @@ echo "Done";
string(16) "some_page_ref123"
}
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(6) {
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
["scheme"]=>
string(4) "http"
["host"]=>
- string(26) "secret@hideout@www.php.net"
+ string(11) "www.php.net"
["port"]=>
int(80)
+ ["user"]=>
+ string(14) "secret@hideout"
["path"]=>
string(10) "/index.php"
["query"]=>
diff --git a/ext/standard/tests/url/parse_url_basic_003.phpt b/ext/standard/tests/url/parse_url_basic_003.phpt
index e7cd24c9698c..8b0e7eb87500 100644
--- a/ext/standard/tests/url/parse_url_basic_003.phpt
+++ b/ext/standard/tests/url/parse_url_basic_003.phpt
@@ -68,7 +68,7 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(26) "secret@hideout@www.php.net"
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> nntp://news.php.net : string(12) "news.php.net"
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : string(11) "ftp.gnu.org"
diff --git a/ext/standard/tests/url/parse_url_basic_005.phpt b/ext/standard/tests/url/parse_url_basic_005.phpt
index 8617d37059ec..a5ca381a691f 100644
--- a/ext/standard/tests/url/parse_url_basic_005.phpt
+++ b/ext/standard/tests/url/parse_url_basic_005.phpt
@@ -68,7 +68,7 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(6) "secret"
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(0) ""
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(6) "secret"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : NULL
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(14) "secret@hideout"
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(6) "secret"
--> nntp://news.php.net : NULL
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : NULL
diff --git a/ext/standard/tests/url/parse_url_unterminated.phpt b/ext/standard/tests/url/parse_url_unterminated.phpt
index 1377cb7735f0..8f46eb3dbedf 100644
--- a/ext/standard/tests/url/parse_url_unterminated.phpt
+++ b/ext/standard/tests/url/parse_url_unterminated.phpt
@@ -508,13 +508,15 @@ echo "Done";
string(16) "some_page_ref123"
}
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(6) {
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
["scheme"]=>
string(4) "http"
["host"]=>
- string(26) "secret@hideout@www.php.net"
+ string(11) "www.php.net"
["port"]=>
int(80)
+ ["user"]=>
+ string(14) "secret@hideout"
["path"]=>
string(10) "/index.php"
["query"]=>
diff --git a/ext/standard/url.c b/ext/standard/url.c
index 157d61466e69..9743a00166b1 100644
--- a/ext/standard/url.c
+++ b/ext/standard/url.c
@@ -260,9 +260,6 @@ parse_host:
ret->pass = zend_string_init(pp, (p-pp), 0);
php_replace_controlchars_ex(ZSTR_VAL(ret->pass), ZSTR_LEN(ret->pass));
} else {
- if (!is_userinfo_valid(s, p-s)) {
- goto check_port;
- }
ret->user = zend_string_init(s, (p-s), 0);
php_replace_controlchars_ex(ZSTR_VAL(ret->user), ZSTR_LEN(ret->user));
}
@@ -271,7 +268,6 @@ parse_host:
s = p + 1;
}
-check_port:
/* check for port */
if (s < ue && *s == '[' && *(e-1) == ']') {
/* Short circuit portscan,

View File

@ -1,58 +0,0 @@
From a5538c62293fa782fcc382d0635cfc0c8b9190e3 Mon Sep 17 00:00:00 2001
From: "Christoph M. Becker" <cmbecker69@gmx.de>
Date: Mon, 14 Jun 2021 13:22:27 +0200
Subject: [PATCH] Fix #81122: SSRF bypass in FILTER_VALIDATE_URL
We need to ensure that the password detected by parse_url() is actually
a valid password; we can re-use is_userinfo_valid() for that.
---
ext/filter/logical_filters.c | 4 +++-
ext/filter/tests/bug81122.phpt | 21 +++++++++++++++++++++
2 files changed, 24 insertions(+), 1 deletion(-)
create mode 100644 ext/filter/tests/bug81122.phpt
diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c
index 1cf345dbb58a..3f314fefa0e1 100644
--- a/ext/filter/logical_filters.c
+++ b/ext/filter/logical_filters.c
@@ -609,7 +609,9 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
RETURN_VALIDATION_FAILED
}
- if (url->user != NULL && !is_userinfo_valid(url->user)) {
+ if (url->user != NULL && !is_userinfo_valid(url->user)
+ || url->pass != NULL && !is_userinfo_valid(url->pass)
+ ) {
php_url_free(url);
RETURN_VALIDATION_FAILED
diff --git a/ext/filter/tests/bug81122.phpt b/ext/filter/tests/bug81122.phpt
new file mode 100644
index 000000000000..d89d4114a547
--- /dev/null
+++ b/ext/filter/tests/bug81122.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #81122 (SSRF bypass in FILTER_VALIDATE_URL)
+--SKIPIF--
+<?php
+if (!extension_loaded('filter')) die("skip filter extension not available");
+?>
+--FILE--
+<?php
+$urls = [
+ "https://example.com:\\@test.com/",
+ "https://user:\\epass@test.com",
+ "https://user:\\@test.com",
+];
+foreach ($urls as $url) {
+ var_dump(filter_var($url, FILTER_VALIDATE_URL));
+}
+?>
+--EXPECT--
+bool(false)
+bool(false)
+bool(false)
--
2.27.0

View File

@ -1,36 +0,0 @@
From 9c673083cd46ee2a954a62156acbe4b6e657c048 Mon Sep 17 00:00:00 2001
From: Stanislav Malyshev <stas@php.net>
Date: Wed, 27 Jan 2021 00:13:43 -0800
Subject: [PATCH] Rm unneeded function
---
ext/standard/url.c | 16 ----------------
1 file changed, 16 deletions(-)
diff --git a/ext/standard/url.c b/ext/standard/url.c
index 9743a00166b1..0b3d97a76691 100644
--- a/ext/standard/url.c
+++ b/ext/standard/url.c
@@ -102,22 +102,6 @@ static const char *binary_strcspn(const char *s, const char *e
return e;
}
-static int is_userinfo_valid(const char *str, size_t len)
-{
- const char *valid = "-._~!$&'()*+,;=:";
- const char *p = str;
- while (p - str < len) {
- if (isalpha(*p) || isdigit(*p) || strchr(valid, *p)) {
- p++;
- } else if (*p == '%' && p - str <= len - 3 && isdigit(*(p+1)) && isxdigit(*(p+2))) {
- p += 3;
- } else {
- return 0;
- }
- }
- return 1;
-}
-
/* {{{ php_url_parse */
PHPAPI php_url *php_url_parse_ex(char const *str, size_t length)
{

View File

@ -1,28 +0,0 @@
From 7bab67c2d0d3c30b1176d5bcf6f20b0e1c50c589 Mon Sep 17 00:00:00 2001
From: Stanislav Malyshev <stas@php.net>
Date: Sun, 27 Jun 2021 21:57:58 -0700
Subject: [PATCH] Fix warning
(cherry picked from commit 190013787bbc424c240413d914e3a038f974ccef)
---
ext/filter/logical_filters.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c
index 721da45d532d..1ffc467b43c1 100644
--- a/ext/filter/logical_filters.c
+++ b/ext/filter/logical_filters.c
@@ -632,8 +632,8 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
RETURN_VALIDATION_FAILED
}
- if (url->user != NULL && !is_userinfo_valid(url->user)
- || url->pass != NULL && !is_userinfo_valid(url->pass)
+ if ((url->user != NULL && !is_userinfo_valid(url->user))
+ || (url->pass != NULL && !is_userinfo_valid(url->pass))
) {
php_url_free(url);
RETURN_VALIDATION_FAILED
--
2.27.0

View File

@ -1,197 +0,0 @@
From 5174de7cd33c3d4fa591c9c93859ff9989b07e8c Mon Sep 17 00:00:00 2001
From: "Christoph M. Becker" <cmbecker69@gmx.de>
Date: Wed, 13 May 2020 09:36:52 +0200
Subject: [PATCH] Fix #77423: parse_url() will deliver a wrong host to user
To avoid that `parse_url()` returns an erroneous host, which would be
valid for `FILTER_VALIDATE_URL`, we make sure that only userinfo which
is valid according to RFC 3986 is treated as such.
For consistency with the existing url parsing code, we use ctype
functions, although that is not necessarily correct.
---
ext/standard/tests/strings/url_t.phpt | 6 ++--
ext/standard/tests/url/bug77423.phpt | 30 +++++++++++++++++++
.../tests/url/parse_url_basic_001.phpt | 6 ++--
.../tests/url/parse_url_basic_003.phpt | 2 +-
.../tests/url/parse_url_basic_005.phpt | 2 +-
.../tests/url/parse_url_unterminated.phpt | 6 ++--
ext/standard/url.c | 24 +++++++++++++--
7 files changed, 60 insertions(+), 16 deletions(-)
create mode 100644 ext/standard/tests/url/bug77423.phpt
diff --git a/ext/standard/tests/strings/url_t.phpt b/ext/standard/tests/strings/url_t.phpt
index caa93cb9cf01..dc13018b14a0 100644
--- a/ext/standard/tests/strings/url_t.phpt
+++ b/ext/standard/tests/strings/url_t.phpt
@@ -589,15 +589,13 @@ $sample_urls = array (
string(16) "some_page_ref123"
}
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(6) {
["scheme"]=>
string(4) "http"
["host"]=>
- string(11) "www.php.net"
+ string(26) "secret@hideout@www.php.net"
["port"]=>
int(80)
- ["user"]=>
- string(14) "secret@hideout"
["path"]=>
string(10) "/index.php"
["query"]=>
diff --git a/ext/standard/tests/url/bug77423.phpt b/ext/standard/tests/url/bug77423.phpt
new file mode 100644
index 000000000000..be03fe95e24e
--- /dev/null
+++ b/ext/standard/tests/url/bug77423.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Bug #77423 (parse_url() will deliver a wrong host to user)
+--FILE--
+<?php
+$urls = array(
+ "http://php.net\@aliyun.com/aaa.do",
+ "https://example.com\uFF03@bing.com",
+);
+foreach ($urls as $url) {
+ var_dump(filter_var($url, FILTER_VALIDATE_URL));
+ var_dump(parse_url($url));
+}
+?>
+--EXPECT--
+bool(false)
+array(3) {
+ ["scheme"]=>
+ string(4) "http"
+ ["host"]=>
+ string(19) "php.net\@aliyun.com"
+ ["path"]=>
+ string(7) "/aaa.do"
+}
+bool(false)
+array(2) {
+ ["scheme"]=>
+ string(5) "https"
+ ["host"]=>
+ string(26) "example.com\uFF03@bing.com"
+}
diff --git a/ext/standard/tests/url/parse_url_basic_001.phpt b/ext/standard/tests/url/parse_url_basic_001.phpt
index f3abd703b263..89b1b7b3d683 100644
--- a/ext/standard/tests/url/parse_url_basic_001.phpt
+++ b/ext/standard/tests/url/parse_url_basic_001.phpt
@@ -514,15 +514,13 @@ echo "Done";
string(16) "some_page_ref123"
}
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(6) {
["scheme"]=>
string(4) "http"
["host"]=>
- string(11) "www.php.net"
+ string(26) "secret@hideout@www.php.net"
["port"]=>
int(80)
- ["user"]=>
- string(14) "secret@hideout"
["path"]=>
string(10) "/index.php"
["query"]=>
diff --git a/ext/standard/tests/url/parse_url_basic_003.phpt b/ext/standard/tests/url/parse_url_basic_003.phpt
index 1eb64d6a1b1c..da35b927821a 100644
--- a/ext/standard/tests/url/parse_url_basic_003.phpt
+++ b/ext/standard/tests/url/parse_url_basic_003.phpt
@@ -62,7 +62,7 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(26) "secret@hideout@www.php.net"
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(11) "www.php.net"
--> nntp://news.php.net : string(12) "news.php.net"
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : string(11) "ftp.gnu.org"
diff --git a/ext/standard/tests/url/parse_url_basic_005.phpt b/ext/standard/tests/url/parse_url_basic_005.phpt
index dfbe7e7971e4..731a4adb3149 100644
--- a/ext/standard/tests/url/parse_url_basic_005.phpt
+++ b/ext/standard/tests/url/parse_url_basic_005.phpt
@@ -62,7 +62,7 @@ echo "Done";
--> http://secret:@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(6) "secret"
--> http://:hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(0) ""
--> http://secret:hideout@www.php.net/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(6) "secret"
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(14) "secret@hideout"
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : NULL
--> http://secret:hid:out@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123 : string(6) "secret"
--> nntp://news.php.net : NULL
--> ftp://ftp.gnu.org/gnu/glic/glibc.tar.gz : NULL
diff --git a/ext/standard/tests/url/parse_url_unterminated.phpt b/ext/standard/tests/url/parse_url_unterminated.phpt
index 7c9150a513d9..f859fc7d407f 100644
--- a/ext/standard/tests/url/parse_url_unterminated.phpt
+++ b/ext/standard/tests/url/parse_url_unterminated.phpt
@@ -522,15 +522,13 @@ echo "Done";
string(16) "some_page_ref123"
}
---> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(7) {
+--> http://secret@hideout@www.php.net:80/index.php?test=1&test2=char&test3=mixesCI#some_page_ref123: array(6) {
["scheme"]=>
string(4) "http"
["host"]=>
- string(11) "www.php.net"
+ string(26) "secret@hideout@www.php.net"
["port"]=>
int(80)
- ["user"]=>
- string(14) "secret@hideout"
["path"]=>
string(10) "/index.php"
["query"]=>
diff --git a/ext/standard/url.c b/ext/standard/url.c
index a33091a86b75..113e0100243a 100644
--- a/ext/standard/url.c
+++ b/ext/standard/url.c
@@ -92,6 +92,22 @@ static const char *binary_strcspn(const char *s, const char *e, const char *char
return e;
}
+static int is_userinfo_valid(const char *str, size_t len)
+{
+ const char *valid = "-._~!$&'()*+,;=:";
+ const char *p = str;
+ while (p - str < len) {
+ if (isalpha(*p) || isdigit(*p) || strchr(valid, *p)) {
+ p++;
+ } else if (*p == '%' && p - str <= len - 3 && isdigit(*(p+1)) && isxdigit(*(p+2))) {
+ p += 3;
+ } else {
+ return 0;
+ }
+ }
+ return 1;
+}
+
/* {{{ php_url_parse */
PHPAPI php_url *php_url_parse_ex(char const *str, size_t length)
{
@@ -233,13 +249,17 @@ PHPAPI php_url *php_url_parse_ex2(char const *str, size_t length, zend_bool *has
ret->pass = zend_string_init(pp, (p-pp), 0);
php_replace_controlchars_ex(ZSTR_VAL(ret->pass), ZSTR_LEN(ret->pass));
} else {
- ret->user = zend_string_init(s, (p-s), 0);
- php_replace_controlchars_ex(ZSTR_VAL(ret->user), ZSTR_LEN(ret->user));
+ if (!is_userinfo_valid(s, p-s)) {
+ goto check_port;
+ }
+ ret->user = zend_string_init(s, (p-s), 0);
+ php_replace_controlchars_ex(ZSTR_VAL(ret->user), ZSTR_LEN(ret->user));
}
s = p + 1;
}
+check_port:
/* check for port */
if (s < ue && *s == '[' && *(e-1) == ']') {
/* Short circuit portscan,

View File

@ -1,396 +0,0 @@
From fadb1f8c1d08ae62b4f0a16917040fde57a3b93b Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Sat, 2 Oct 2021 22:53:41 +0100
Subject: [PATCH] Fix bug #81026 (PHP-FPM oob R/W in root process leading to
priv escalation)
The main change is to store scoreboard procs directly to the variable sized
array rather than indirectly through the pointer.
Signed-off-by: Stanislav Malyshev <stas@php.net>
---
sapi/fpm/fpm/fpm_children.c | 14 ++---
sapi/fpm/fpm/fpm_request.c | 4 +-
sapi/fpm/fpm/fpm_scoreboard.c | 106 ++++++++++++++++++++-------------
sapi/fpm/fpm/fpm_scoreboard.h | 11 ++--
sapi/fpm/fpm/fpm_status.c | 4 +-
sapi/fpm/fpm/fpm_worker_pool.c | 2 +-
6 files changed, 81 insertions(+), 60 deletions(-)
diff --git a/sapi/fpm/fpm/fpm_children.c b/sapi/fpm/fpm/fpm_children.c
index fe92bdf1b0c0..a24e06fc483b 100644
--- a/sapi/fpm/fpm/fpm_children.c
+++ b/sapi/fpm/fpm/fpm_children.c
@@ -246,7 +246,7 @@ void fpm_children_bury() /* {{{ */
fpm_child_unlink(child);
- fpm_scoreboard_proc_free(wp->scoreboard, child->scoreboard_i);
+ fpm_scoreboard_proc_free(child);
fpm_clock_get(&tv1);
@@ -256,9 +256,9 @@ void fpm_children_bury() /* {{{ */
if (!fpm_pctl_can_spawn_children()) {
severity = ZLOG_DEBUG;
}
- zlog(severity, "[pool %s] child %d exited %s after %ld.%06d seconds from start", child->wp->config->name, (int) pid, buf, tv2.tv_sec, (int) tv2.tv_usec);
+ zlog(severity, "[pool %s] child %d exited %s after %ld.%06d seconds from start", wp->config->name, (int) pid, buf, tv2.tv_sec, (int) tv2.tv_usec);
} else {
- zlog(ZLOG_DEBUG, "[pool %s] child %d has been killed by the process management after %ld.%06d seconds from start", child->wp->config->name, (int) pid, tv2.tv_sec, (int) tv2.tv_usec);
+ zlog(ZLOG_DEBUG, "[pool %s] child %d has been killed by the process management after %ld.%06d seconds from start", wp->config->name, (int) pid, tv2.tv_sec, (int) tv2.tv_usec);
}
fpm_child_close(child, 1 /* in event_loop */);
@@ -324,7 +324,7 @@ static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp) /
return 0;
}
- if (0 > fpm_scoreboard_proc_alloc(wp->scoreboard, &c->scoreboard_i)) {
+ if (0 > fpm_scoreboard_proc_alloc(c)) {
fpm_stdio_discard_pipes(c);
fpm_child_free(c);
return 0;
@@ -336,7 +336,7 @@ static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp) /
static void fpm_resources_discard(struct fpm_child_s *child) /* {{{ */
{
- fpm_scoreboard_proc_free(child->wp->scoreboard, child->scoreboard_i);
+ fpm_scoreboard_proc_free(child);
fpm_stdio_discard_pipes(child);
fpm_child_free(child);
}
@@ -349,10 +349,10 @@ static void fpm_child_resources_use(struct fpm_child_s *child) /* {{{ */
if (wp == child->wp || wp == child->wp->shared) {
continue;
}
- fpm_scoreboard_free(wp->scoreboard);
+ fpm_scoreboard_free(wp);
}
- fpm_scoreboard_child_use(child->wp->scoreboard, child->scoreboard_i, getpid());
+ fpm_scoreboard_child_use(child, getpid());
fpm_stdio_child_use_pipes(child);
fpm_child_free(child);
}
diff --git a/sapi/fpm/fpm/fpm_request.c b/sapi/fpm/fpm/fpm_request.c
index c80aa144628f..0a6f6a7cfbf0 100644
--- a/sapi/fpm/fpm/fpm_request.c
+++ b/sapi/fpm/fpm/fpm_request.c
@@ -285,7 +285,7 @@ int fpm_request_is_idle(struct fpm_child_s *child) /* {{{ */
struct fpm_scoreboard_proc_s *proc;
/* no need in atomicity here */
- proc = fpm_scoreboard_proc_get(child->wp->scoreboard, child->scoreboard_i);
+ proc = fpm_scoreboard_proc_get_from_child(child);
if (!proc) {
return 0;
}
@@ -300,7 +300,7 @@ int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv) /*
if (!tv) return -1;
- proc = fpm_scoreboard_proc_get(child->wp->scoreboard, child->scoreboard_i);
+ proc = fpm_scoreboard_proc_get_from_child(child);
if (!proc) {
return -1;
}
diff --git a/sapi/fpm/fpm/fpm_scoreboard.c b/sapi/fpm/fpm/fpm_scoreboard.c
index fe36a9755bba..5380d184901b 100644
--- a/sapi/fpm/fpm/fpm_scoreboard.c
+++ b/sapi/fpm/fpm/fpm_scoreboard.c
@@ -6,6 +6,7 @@
#include <time.h>
#include "fpm_config.h"
+#include "fpm_children.h"
#include "fpm_scoreboard.h"
#include "fpm_shm.h"
#include "fpm_sockets.h"
@@ -23,7 +24,6 @@ static float fpm_scoreboard_tick;
int fpm_scoreboard_init_main() /* {{{ */
{
struct fpm_worker_pool_s *wp;
- unsigned int i;
#ifdef HAVE_TIMES
#if (defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK))
@@ -40,7 +40,7 @@ int fpm_scoreboard_init_main() /* {{{ */
for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
- size_t scoreboard_size, scoreboard_nprocs_size;
+ size_t scoreboard_procs_size;
void *shm_mem;
if (wp->config->pm_max_children < 1) {
@@ -53,22 +53,15 @@ int fpm_scoreboard_init_main() /* {{{ */
return -1;
}
- scoreboard_size = sizeof(struct fpm_scoreboard_s) + (wp->config->pm_max_children) * sizeof(struct fpm_scoreboard_proc_s *);
- scoreboard_nprocs_size = sizeof(struct fpm_scoreboard_proc_s) * wp->config->pm_max_children;
- shm_mem = fpm_shm_alloc(scoreboard_size + scoreboard_nprocs_size);
+ scoreboard_procs_size = sizeof(struct fpm_scoreboard_proc_s) * wp->config->pm_max_children;
+ shm_mem = fpm_shm_alloc(sizeof(struct fpm_scoreboard_s) + scoreboard_procs_size);
if (!shm_mem) {
return -1;
}
- wp->scoreboard = shm_mem;
+ wp->scoreboard = shm_mem;
+ wp->scoreboard->pm = wp->config->pm;
wp->scoreboard->nprocs = wp->config->pm_max_children;
- shm_mem += scoreboard_size;
-
- for (i = 0; i < wp->scoreboard->nprocs; i++, shm_mem += sizeof(struct fpm_scoreboard_proc_s)) {
- wp->scoreboard->procs[i] = shm_mem;
- }
-
- wp->scoreboard->pm = wp->config->pm;
wp->scoreboard->start_epoch = time(NULL);
strlcpy(wp->scoreboard->pool, wp->config->name, sizeof(wp->scoreboard->pool));
@@ -167,28 +160,48 @@ struct fpm_scoreboard_s *fpm_scoreboard_get() /* {{{*/
}
/* }}} */
-struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{*/
+static inline struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_ex(
+ struct fpm_scoreboard_s *scoreboard, int child_index, unsigned int nprocs) /* {{{*/
{
if (!scoreboard) {
- scoreboard = fpm_scoreboard;
+ return NULL;
}
- if (!scoreboard) {
+ if (child_index < 0 || (unsigned int)child_index >= nprocs) {
return NULL;
}
+ return &scoreboard->procs[child_index];
+}
+/* }}} */
+
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(
+ struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{*/
+{
+ if (!scoreboard) {
+ scoreboard = fpm_scoreboard;
+ }
+
if (child_index < 0) {
child_index = fpm_scoreboard_i;
}
- if (child_index < 0 || (unsigned int)child_index >= scoreboard->nprocs) {
- return NULL;
- }
+ return fpm_scoreboard_proc_get_ex(scoreboard, child_index, scoreboard->nprocs);
+}
+/* }}} */
- return scoreboard->procs[child_index];
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_from_child(struct fpm_child_s *child) /* {{{*/
+{
+ struct fpm_worker_pool_s *wp = child->wp;
+ unsigned int nprocs = wp->config->pm_max_children;
+ struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
+ int child_index = child->scoreboard_i;
+
+ return fpm_scoreboard_proc_get_ex(scoreboard, child_index, nprocs);
}
/* }}} */
+
struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang) /* {{{ */
{
struct fpm_scoreboard_s *s;
@@ -239,28 +252,28 @@ void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc) /* {{{ */
proc->lock = 0;
}
-void fpm_scoreboard_free(struct fpm_scoreboard_s *scoreboard) /* {{{ */
+void fpm_scoreboard_free(struct fpm_worker_pool_s *wp) /* {{{ */
{
- size_t scoreboard_size, scoreboard_nprocs_size;
+ size_t scoreboard_procs_size;
+ struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
if (!scoreboard) {
zlog(ZLOG_ERROR, "**scoreboard is NULL");
return;
}
- scoreboard_size = sizeof(struct fpm_scoreboard_s) + (scoreboard->nprocs) * sizeof(struct fpm_scoreboard_proc_s *);
- scoreboard_nprocs_size = sizeof(struct fpm_scoreboard_proc_s) * scoreboard->nprocs;
+ scoreboard_procs_size = sizeof(struct fpm_scoreboard_proc_s) * wp->config->pm_max_children;
- fpm_shm_free(scoreboard, scoreboard_size + scoreboard_nprocs_size);
+ fpm_shm_free(scoreboard, sizeof(struct fpm_scoreboard_s) + scoreboard_procs_size);
}
/* }}} */
-void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_index, pid_t pid) /* {{{ */
+void fpm_scoreboard_child_use(struct fpm_child_s *child, pid_t pid) /* {{{ */
{
struct fpm_scoreboard_proc_s *proc;
- fpm_scoreboard = scoreboard;
- fpm_scoreboard_i = child_index;
- proc = fpm_scoreboard_proc_get(scoreboard, child_index);
+ fpm_scoreboard = child->wp->scoreboard;
+ fpm_scoreboard_i = child->scoreboard_i;
+ proc = fpm_scoreboard_proc_get_from_child(child);
if (!proc) {
return;
}
@@ -269,18 +282,22 @@ void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_ind
}
/* }}} */
-void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{ */
+void fpm_scoreboard_proc_free(struct fpm_child_s *child) /* {{{ */
{
+ struct fpm_worker_pool_s *wp = child->wp;
+ struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
+ int child_index = child->scoreboard_i;
+
if (!scoreboard) {
return;
}
- if (child_index < 0 || (unsigned int)child_index >= scoreboard->nprocs) {
+ if (child_index < 0 || child_index >= wp->config->pm_max_children) {
return;
}
- if (scoreboard->procs[child_index] && scoreboard->procs[child_index]->used > 0) {
- memset(scoreboard->procs[child_index], 0, sizeof(struct fpm_scoreboard_proc_s));
+ if (scoreboard->procs[child_index].used > 0) {
+ memset(&scoreboard->procs[child_index], 0, sizeof(struct fpm_scoreboard_proc_s));
}
/* set this slot as free to avoid search on next alloc */
@@ -288,41 +305,44 @@ void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_ind
}
/* }}} */
-int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_index) /* {{{ */
+int fpm_scoreboard_proc_alloc(struct fpm_child_s *child) /* {{{ */
{
int i = -1;
+ struct fpm_worker_pool_s *wp = child->wp;
+ struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
+ int nprocs = wp->config->pm_max_children;
- if (!scoreboard || !child_index) {
+ if (!scoreboard) {
return -1;
}
/* first try the slot which is supposed to be free */
- if (scoreboard->free_proc >= 0 && (unsigned int)scoreboard->free_proc < scoreboard->nprocs) {
- if (scoreboard->procs[scoreboard->free_proc] && !scoreboard->procs[scoreboard->free_proc]->used) {
+ if (scoreboard->free_proc >= 0 && scoreboard->free_proc < nprocs) {
+ if (!scoreboard->procs[scoreboard->free_proc].used) {
i = scoreboard->free_proc;
}
}
if (i < 0) { /* the supposed free slot is not, let's search for a free slot */
zlog(ZLOG_DEBUG, "[pool %s] the proc->free_slot was not free. Let's search", scoreboard->pool);
- for (i = 0; i < (int)scoreboard->nprocs; i++) {
- if (scoreboard->procs[i] && !scoreboard->procs[i]->used) { /* found */
+ for (i = 0; i < nprocs; i++) {
+ if (!scoreboard->procs[i].used) { /* found */
break;
}
}
}
/* no free slot */
- if (i < 0 || i >= (int)scoreboard->nprocs) {
+ if (i < 0 || i >= nprocs) {
zlog(ZLOG_ERROR, "[pool %s] no free scoreboard slot", scoreboard->pool);
return -1;
}
- scoreboard->procs[i]->used = 1;
- *child_index = i;
+ scoreboard->procs[i].used = 1;
+ child->scoreboard_i = i;
/* supposed next slot is free */
- if (i + 1 >= (int)scoreboard->nprocs) {
+ if (i + 1 >= nprocs) {
scoreboard->free_proc = 0;
} else {
scoreboard->free_proc = i + 1;
diff --git a/sapi/fpm/fpm/fpm_scoreboard.h b/sapi/fpm/fpm/fpm_scoreboard.h
index 060eddea982a..363bdd260547 100644
--- a/sapi/fpm/fpm/fpm_scoreboard.h
+++ b/sapi/fpm/fpm/fpm_scoreboard.h
@@ -64,7 +64,7 @@ struct fpm_scoreboard_s {
int free_proc;
unsigned long int slow_rq;
struct fpm_scoreboard_s *shared;
- struct fpm_scoreboard_proc_s *procs[];
+ struct fpm_scoreboard_proc_s procs[];
};
int fpm_scoreboard_init_main();
@@ -73,18 +73,19 @@ int fpm_scoreboard_init_child(struct fpm_worker_pool_s *wp);
void fpm_scoreboard_update(int idle, int active, int lq, int lq_len, int requests, int max_children_reached, int slow_rq, int action, struct fpm_scoreboard_s *scoreboard);
struct fpm_scoreboard_s *fpm_scoreboard_get();
struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(struct fpm_scoreboard_s *scoreboard, int child_index);
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_from_child(struct fpm_child_s *child);
struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang);
void fpm_scoreboard_release(struct fpm_scoreboard_s *scoreboard);
struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s *scoreboard, int child_index, int nohang);
void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc);
-void fpm_scoreboard_free(struct fpm_scoreboard_s *scoreboard);
+void fpm_scoreboard_free(struct fpm_worker_pool_s *wp);
-void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_index, pid_t pid);
+void fpm_scoreboard_child_use(struct fpm_child_s *child, pid_t pid);
-void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_index);
-int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_index);
+void fpm_scoreboard_proc_free(struct fpm_child_s *child);
+int fpm_scoreboard_proc_alloc(struct fpm_child_s *child);
#ifdef HAVE_TIMES
float fpm_scoreboard_get_tick();
diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c
index ecce6e32c4a6..0d74f551b797 100644
--- a/sapi/fpm/fpm/fpm_status.c
+++ b/sapi/fpm/fpm/fpm_status.c
@@ -472,10 +472,10 @@ int fpm_status_handle_request(void) /* {{{ */
first = 1;
for (i=0; i<scoreboard_p->nprocs; i++) {
- if (!scoreboard_p->procs[i] || !scoreboard_p->procs[i]->used) {
+ if (!scoreboard_p->procs[i].used) {
continue;
}
- proc = *scoreboard_p->procs[i];
+ proc = scoreboard_p->procs[i];
if (first) {
first = 0;
diff --git a/sapi/fpm/fpm/fpm_worker_pool.c b/sapi/fpm/fpm/fpm_worker_pool.c
index d04528f4e0d0..65a9b226b1ae 100644
--- a/sapi/fpm/fpm/fpm_worker_pool.c
+++ b/sapi/fpm/fpm/fpm_worker_pool.c
@@ -54,7 +54,7 @@ static void fpm_worker_pool_cleanup(int which, void *arg) /* {{{ */
fpm_worker_pool_config_free(wp->config);
fpm_children_free(wp->children);
if ((which & FPM_CLEANUP_CHILD) == 0 && fpm_globals.parent_pid == getpid()) {
- fpm_scoreboard_free(wp->scoreboard);
+ fpm_scoreboard_free(wp);
}
fpm_worker_pool_free(wp);
}

View File

@ -1,68 +0,0 @@
From 08da7c73726f7b86b67d6f0ff87c73c585a7834a Mon Sep 17 00:00:00 2001
From: "Christoph M. Becker" <cmbecker69@gmx.de>
Date: Fri, 30 Apr 2021 13:53:21 +0200
Subject: [PATCH] Fix #76449: SIGSEGV in firebird_handle_doer
We need to verify that the `result_size` is not larger than our buffer,
and also should make sure that the `len` which is passed to
`isc_vax_integer()` has a permissible value; otherwise we bail out.
---
ext/pdo_firebird/firebird_driver.c | 10 ++++++++++
ext/pdo_firebird/tests/bug_76449.phpt | 23 +++++++++++++++++++++++
2 files changed, 33 insertions(+)
create mode 100644 ext/pdo_firebird/tests/bug_76449.phpt
diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c
index c27a9e2e..303e0f47 100644
--- a/ext/pdo_firebird/firebird_driver.c
+++ b/ext/pdo_firebird/firebird_driver.c
@@ -626,8 +626,18 @@ static zend_long firebird_handle_doer(pdo_dbh_t *dbh, const char *sql, size_t sq
if (result[0] == isc_info_sql_records) {
unsigned i = 3, result_size = isc_vax_integer(&result[1],2);
+ if (result_size > sizeof(result)) {
+ ret = -1;
+ goto free_statement;
+ }
+
while (result[i] != isc_info_end && i < result_size) {
short len = (short)isc_vax_integer(&result[i+1],2);
+ /* bail out on bad len */
+ if (len != 1 && len != 2 && len != 4) {
+ ret = -1;
+ goto free_statement;
+ }
if (result[i] != isc_info_req_select_count) {
ret += isc_vax_integer(&result[i+3],len);
}
diff --git a/ext/pdo_firebird/tests/bug_76449.phpt b/ext/pdo_firebird/tests/bug_76449.phpt
new file mode 100644
index 00000000..48a09c1d
--- /dev/null
+++ b/ext/pdo_firebird/tests/bug_76449.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #76449 (SIGSEGV in firebird_handle_doer)
+--SKIPIF--
+<?php
+if (!extension_loaded('pdo_firebird')) die("skip pdo_firebird extension not available");
+if (!extension_loaded('sockets')) die("skip sockets extension not available");
+?>
+--FILE--
+<?php
+require_once "payload_server.inc";
+
+$address = run_server(__DIR__ . "/bug_76449.data");
+
+// no need to change the credentials; we're running against a fake server
+$dsn = "firebird:dbname=inet://$address/test";
+$username = 'SYSDBA';
+$password = 'masterkey';
+
+$dbh = new PDO($dsn, $username, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
+var_dump($dbh->exec("INSERT INTO test VALUES ('hihi2', 'xxxxx')"));
+?>
+--EXPECT--
+bool(false)
--
2.27.0

View File

@ -1,131 +0,0 @@
From f15f8fc573eb38c3c73e23e0930063a6f6409ed4 Mon Sep 17 00:00:00 2001
From: "Christoph M. Becker" <cmbecker69@gmx.de>
Date: Tue, 1 Sep 2020 10:04:28 +0200
Subject: [PATCH] Fix #79971: special character is breaking the path in xml
function
The libxml based XML functions accepting a filename actually accept
URIs with possibly percent-encoded characters. Percent-encoded NUL
bytes lead to truncation, like non-encoded NUL bytes would. We catch
those, and let the functions fail with a respective warning.
---
ext/dom/domimplementation.c | 5 +++++
ext/dom/tests/bug79971_2.phpt | 20 ++++++++++++++++++++
ext/libxml/libxml.c | 9 +++++++++
ext/simplexml/tests/bug79971_1.phpt | 27 +++++++++++++++++++++++++++
ext/simplexml/tests/bug79971_1.xml | 2 ++
5 files changed, 63 insertions(+)
create mode 100644 ext/dom/tests/bug79971_2.phpt
create mode 100644 ext/simplexml/tests/bug79971_1.phpt
create mode 100644 ext/simplexml/tests/bug79971_1.xml
diff --git a/ext/dom/domimplementation.c b/ext/dom/domimplementation.c
index c6629c85e965..6f59f9c7c89f 100644
--- a/ext/dom/domimplementation.c
+++ b/ext/dom/domimplementation.c
@@ -112,6 +112,11 @@ PHP_METHOD(domimplementation, createDocumentType)
pch2 = (xmlChar *) systemid;
}
+ if (strstr(name, "%00")) {
+ php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes");
+ RETURN_FALSE;
+ }
+
uri = xmlParseURI(name);
if (uri != NULL && uri->opaque != NULL) {
localname = xmlStrdup((xmlChar *) uri->opaque);
diff --git a/ext/dom/tests/bug79971_2.phpt b/ext/dom/tests/bug79971_2.phpt
new file mode 100644
index 000000000000..c4e6b1e4e093
--- /dev/null
+++ b/ext/dom/tests/bug79971_2.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #79971 (special character is breaking the path in xml function)
+--SKIPIF--
+<?php
+if (!extension_loaded('dom')) die('skip dom extension not available');
+?>
+--FILE--
+<?php
+$imp = new DOMImplementation;
+if (PHP_OS_FAMILY === 'Windows') {
+ $path = '/' . str_replace('\\', '/', __DIR__);
+} else {
+ $path = __DIR__;
+}
+$uri = "file://$path/bug79971_2.xml";
+var_dump($imp->createDocumentType("$uri%00foo"));
+?>
+--EXPECTF--
+Warning: DOMImplementation::createDocumentType(): URI must not contain percent-encoded NUL bytes in %s on line %d
+bool(false)
diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c
index 2be6a5b47a06..72ceea63274c 100644
--- a/ext/libxml/libxml.c
+++ b/ext/libxml/libxml.c
@@ -306,6 +306,10 @@ static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char
int isescaped=0;
xmlURI *uri;
+ if (strstr(filename, "%00")) {
+ php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes");
+ return NULL;
+ }
uri = xmlParseURI(filename);
if (uri && (uri->scheme == NULL ||
@@ -437,6 +441,11 @@ php_libxml_output_buffer_create_filename(const char *URI,
if (URI == NULL)
return(NULL);
+ if (strstr(URI, "%00")) {
+ php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes");
+ return NULL;
+ }
+
puri = xmlParseURI(URI);
if (puri != NULL) {
if (puri->scheme != NULL)
diff --git a/ext/simplexml/tests/bug79971_1.phpt b/ext/simplexml/tests/bug79971_1.phpt
new file mode 100644
index 000000000000..197776d82d38
--- /dev/null
+++ b/ext/simplexml/tests/bug79971_1.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Bug #79971 (special character is breaking the path in xml function)
+--SKIPIF--
+<?php
+if (!extension_loaded('simplexml')) die('skip simplexml extension not available');
+?>
+--FILE--
+<?php
+if (PHP_OS_FAMILY === 'Windows') {
+ $path = '/' . str_replace('\\', '/', __DIR__);
+} else {
+ $path = __DIR__;
+}
+$uri = "file://$path/bug79971_1.xml";
+var_dump(simplexml_load_file("$uri%00foo"));
+
+$sxe = simplexml_load_file($uri);
+var_dump($sxe->asXML("$uri.out%00foo"));
+?>
+--EXPECTF--
+Warning: simplexml_load_file(): URI must not contain percent-encoded NUL bytes in %s on line %d
+
+Warning: simplexml_load_file(): I/O warning : failed to load external entity "%s/bug79971_1.xml%00foo" in %s on line %d
+bool(false)
+
+Warning: SimpleXMLElement::asXML(): URI must not contain percent-encoded NUL bytes in %s on line %d
+bool(false)
diff --git a/ext/simplexml/tests/bug79971_1.xml b/ext/simplexml/tests/bug79971_1.xml
new file mode 100644
index 000000000000..912bb76d9d7e
--- /dev/null
+++ b/ext/simplexml/tests/bug79971_1.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<root></root>

View File

@ -1,54 +0,0 @@
From 82f1bf1b6bc3a43aba62214870e6d0931e93a6d9 Mon Sep 17 00:00:00 2001
From: "Christoph M. Becker" <cmbecker69@gmx.de>
Date: Mon, 31 Jan 2022 15:43:24 +0100
Subject: [PATCH] Fix #81708: UAF due to php_filter_float() failing for ints
We must only release the zval, if we actually assign a new zval.
---
ext/filter/logical_filters.c | 2 +-
ext/filter/tests/bug81708.phpt | 20 ++++++++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
create mode 100644 ext/filter/tests/bug81708.phpt
diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c
index 1bf7c00d13c6..95f7a99e34b1 100644
--- a/ext/filter/logical_filters.c
+++ b/ext/filter/logical_filters.c
@@ -436,10 +436,10 @@ void php_filter_float(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
switch (is_numeric_string(num, p - num, &lval, &dval, 0)) {
case IS_LONG:
- zval_ptr_dtor(value);
if ((min_range_set && (lval < min_range)) || (max_range_set && (lval > max_range))) {
goto error;
}
+ zval_ptr_dtor(value);
ZVAL_DOUBLE(value, (double)lval);
break;
case IS_DOUBLE:
diff --git a/ext/filter/tests/bug81708.phpt b/ext/filter/tests/bug81708.phpt
new file mode 100644
index 000000000000..d0036af13682
--- /dev/null
+++ b/ext/filter/tests/bug81708.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #81708 (UAF due to php_filter_float() failing for ints)
+--SKIPIF--
+<?php
+if (!extension_loaded("filter")) die("skip filter extension not available");
+?>
+--INI--
+opcache.enable_cli=0
+--FILE--
+<?php
+$input = "+" . str_repeat("1", 2); // avoid string interning
+filter_var(
+ $input,
+ FILTER_VALIDATE_FLOAT,
+ ["options" => ['min_range' => -1, 'max_range' => 1]]
+);
+var_dump($input);
+?>
+--EXPECT--
+string(3) "+11"

View File

@ -1,68 +0,0 @@
From 55f6895f4b4c677272fd4ee1113acdbd99c4b5ab Mon Sep 17 00:00:00 2001
From: "Christoph M. Becker" <cmbecker69@gmx.de>
Date: Tue, 17 May 2022 12:59:23 +0200
Subject: [PATCH] Fix #81720: Uninitialized array in pg_query_params() leading
to RCE
We must not free parameters which we haven't initialized yet.
We also fix the not directly related issue, that we checked for the
wrong value being `NULL`, potentially causing a segfault.
---
ext/pgsql/pgsql.c | 6 +++---
ext/pgsql/tests/bug81720.phpt | 27 +++++++++++++++++++++++++++
2 files changed, 30 insertions(+), 3 deletions(-)
create mode 100644 ext/pgsql/tests/bug81720.phpt
--- a/ext/pgsql/pgsql.c
+++ b/ext/pgsql/pgsql.c
@@ -1201,7 +1201,7 @@ PHP_FUNCTION(pg_query_params)
} else {
zend_string *param_str = zval_try_get_string(tmp);
if (!param_str) {
- _php_pgsql_free_params(params, num_params);
+ _php_pgsql_free_params(params, i);
RETURN_THROWS();
}
params[i] = estrndup(ZSTR_VAL(param_str), ZSTR_LEN(param_str));
@@ -3918,8 +3918,8 @@ PHP_FUNCTION(pg_send_execute)
params[i] = NULL;
} else {
zend_string *tmp_str = zval_try_get_string(tmp);
- if (UNEXPECTED(!tmp)) {
- _php_pgsql_free_params(params, num_params);
+ if (UNEXPECTED(!tmp_str)) {
+ _php_pgsql_free_params(params, i);
return;
}
params[i] = estrndup(ZSTR_VAL(tmp_str), ZSTR_LEN(tmp_str));
--- /dev/null
+++ b/ext/pgsql/tests/bug81720.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Bug #81720 (Uninitialized array in pg_query_params() leading to RCE)
+--SKIPIF--
+<?php include("skipif.inc"); ?>
+--FILE--
+<?php
+include('config.inc');
+
+$conn = pg_connect($conn_str);
+
+try {
+ pg_query_params($conn, 'SELECT $1, $2', [1, new stdClass()]);
+} catch (Throwable $ex) {
+ echo $ex->getMessage(), PHP_EOL;
+}
+
+try {
+ pg_send_prepare($conn, "my_query", 'SELECT $1, $2');
+ pg_get_result($conn);
+ pg_send_execute($conn, "my_query", [1, new stdClass()]);
+} catch (Throwable $ex) {
+ echo $ex->getMessage(), PHP_EOL;
+}
+?>
+--EXPECT--
+Object of class stdClass could not be converted to string
+Object of class stdClass could not be converted to string

View File

@ -1,21 +0,0 @@
From 58006537fc5f133ae8549efe5118cde418b3ace9 Mon Sep 17 00:00:00 2001
From: Stanislav Malyshev <smalyshev@gmail.com>
Date: Mon, 6 Jun 2022 00:56:51 -0600
Subject: [PATCH] Fix bug #81719: mysqlnd/pdo password buffer overflow
---
ext/mysqlnd/mysqlnd_wireprotocol.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ b/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -768,7 +768,8 @@ php_mysqlnd_change_auth_response_write(M
MYSQLND_VIO * vio = conn->vio;
MYSQLND_STATS * stats = conn->stats;
MYSQLND_CONNECTION_STATE * connection_state = &conn->state;
- zend_uchar * const buffer = pfc->cmd_buffer.length >= packet->auth_data_len? pfc->cmd_buffer.buffer : mnd_emalloc(packet->auth_data_len);
+ size_t total_packet_size = packet->auth_data_len + MYSQLND_HEADER_SIZE;
+ zend_uchar * const buffer = pfc->cmd_buffer.length >= total_packet_size? pfc->cmd_buffer.buffer : mnd_emalloc(total_packet_size);
zend_uchar * p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */
DBG_ENTER("php_mysqlnd_change_auth_response_write");

View File

@ -5,6 +5,8 @@ Add support for use of the system timezone database, rather
than embedding a copy. Discussed upstream but was not desired.
History:
r21: retrieve tzdata version from /usr/share/zoneinfo/tzdata.zi
r20: adapt for timelib 2020.03 (in 8.0.10RC1)
r19: adapt for timelib 2020.02 (in 8.0.0beta2)
r18: adapt for autotool change in 7.3.3RC1
r17: adapt for timelib 2018.01 (in 7.3.2RC1)
@ -30,9 +32,10 @@ r3: fix a crash if /usr/share/zoneinfo doesn't exist (Raphael Geissert)
r2: add filesystem trawl to set up name alias index
r1: initial revision
diff -up php-8.0.0beta3/ext/date/config0.m4.systzdata php-8.0.0beta3/ext/date/config0.m4
--- php-8.0.0beta3/ext/date/config0.m4.systzdata 2020-09-01 19:13:26.000000000 +0200
+++ php-8.0.0beta3/ext/date/config0.m4 2020-09-02 08:07:51.039979873 +0200
diff --git a/ext/date/config0.m4 b/ext/date/config0.m4
index 20e4164aaa..a61243646d 100644
--- a/ext/date/config0.m4
+++ b/ext/date/config0.m4
@@ -4,6 +4,19 @@ AC_CHECK_HEADERS([io.h])
dnl Check for strtoll, atoll
AC_CHECK_FUNCS(strtoll atoll)
@ -53,9 +56,10 @@ diff -up php-8.0.0beta3/ext/date/config0.m4.systzdata php-8.0.0beta3/ext/date/co
PHP_DATE_CFLAGS="-I@ext_builddir@/lib -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -DHAVE_TIMELIB_CONFIG_H=1"
timelib_sources="lib/astro.c lib/dow.c lib/parse_date.c lib/parse_tz.c
lib/timelib.c lib/tm2unixtime.c lib/unixtime2tm.c lib/parse_iso_intervals.c lib/interval.c"
diff -up php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata php-8.0.0beta3/ext/date/lib/parse_tz.c
--- php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata 2020-09-01 19:13:26.000000000 +0200
+++ php-8.0.0beta3/ext/date/lib/parse_tz.c 2020-09-02 08:07:51.039979873 +0200
diff --git a/ext/date/lib/parse_tz.c b/ext/date/lib/parse_tz.c
index e9bd0f136d..c04ff01adc 100644
--- a/ext/date/lib/parse_tz.c
+++ b/ext/date/lib/parse_tz.c
@@ -26,8 +26,21 @@
#include "timelib.h"
#include "timelib_private.h"
@ -78,7 +82,7 @@ diff -up php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata php-8.0.0beta3/ext/dat
#if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
# if defined(__LITTLE_ENDIAN__)
@@ -94,6 +107,11 @@ static int read_php_preamble(const unsig
@@ -94,6 +107,11 @@ static int read_php_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
{
uint32_t version;
@ -90,7 +94,7 @@ diff -up php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata php-8.0.0beta3/ext/dat
/* read ID */
version = (*tzf)[3] - '0';
*tzf += 4;
@@ -418,7 +436,429 @@ void timelib_dump_tzinfo(timelib_tzinfo
@@ -435,7 +453,467 @@ void timelib_dump_tzinfo(timelib_tzinfo *tz)
}
}
@ -321,6 +325,44 @@ diff -up php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata php-8.0.0beta3/ext/dat
+}
+
+
+/* Retrieve tzdata version. */
+static void retrieve_zone_version(timelib_tzdb *db)
+{
+ static char buf[30];
+ char path[PATH_MAX];
+ FILE *fp;
+
+ strncpy(path, ZONEINFO_PREFIX "/tzdata.zi", sizeof(path));
+
+ fp = fopen(path, "r");
+ if (fp) {
+ if (fgets(buf, sizeof(buf), fp)) {
+ if (!memcmp(buf, "# version ", 10) &&
+ isdigit(buf[10]) &&
+ isdigit(buf[11]) &&
+ isdigit(buf[12]) &&
+ isdigit(buf[13]) &&
+ islower(buf[14])) {
+ if (buf[14] >= 't') { /* 2022t = 2022.20 */
+ buf[17] = 0;
+ buf[16] = buf[14] - 't' + '0';
+ buf[15] = '2';
+ } else if (buf[14] >= 'j') { /* 2022j = 2022.10 */
+ buf[17] = 0;
+ buf[16] = buf[14] - 'j' + '0';
+ buf[15] = '1';
+ } else { /* 2022a = 2022.1 */
+ buf[16] = 0;
+ buf[15] = buf[14] - 'a' + '1';
+ }
+ buf[14] = '.';
+ db->version = buf+10;
+ }
+ }
+ fclose(fp);
+ }
+}
+
+/* Create the zone identifier index by trawling the filesystem. */
+static void create_zone_index(timelib_tzdb *db)
+{
@ -521,7 +563,7 @@ diff -up php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata php-8.0.0beta3/ext/dat
{
int left = 0, right = tzdb->index_size - 1;
@@ -444,9 +884,48 @@ static int seek_to_tz_position(const uns
@@ -461,9 +939,49 @@ static int seek_to_tz_position(const unsigned char **tzf, const char *timezone,
return 0;
}
@ -558,6 +600,7 @@ diff -up php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata php-8.0.0beta3/ext/dat
+ tmp->version = "0.system";
+ tmp->data = NULL;
+ create_zone_index(tmp);
+ retrieve_zone_version(tmp);
+ system_location_table = create_location_table();
+ fake_data_segment(tmp, system_location_table);
+ timezonedb_system = tmp;
@ -570,7 +613,7 @@ diff -up php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata php-8.0.0beta3/ext/dat
}
const timelib_tzdb_index_entry *timelib_timezone_identifiers_list(const timelib_tzdb *tzdb, int *count)
@@ -458,7 +937,30 @@ const timelib_tzdb_index_entry *timelib_
@@ -475,7 +993,30 @@ const timelib_tzdb_index_entry *timelib_timezone_identifiers_list(const timelib_
int timelib_timezone_id_is_valid(const char *timezone, const timelib_tzdb *tzdb)
{
const unsigned char *tzf;
@ -602,7 +645,7 @@ diff -up php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata php-8.0.0beta3/ext/dat
}
static int skip_64bit_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
@@ -500,12 +1002,14 @@ static timelib_tzinfo* timelib_tzinfo_ct
@@ -517,6 +1058,8 @@ static timelib_tzinfo* timelib_tzinfo_ctor(const char *name)
timelib_tzinfo *timelib_parse_tzfile(const char *timezone, const timelib_tzdb *tzdb, int *error_code)
{
const unsigned char *tzf;
@ -611,14 +654,16 @@ diff -up php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata php-8.0.0beta3/ext/dat
timelib_tzinfo *tmp;
int version;
int transitions_result, types_result;
unsigned int type; /* TIMELIB_TZINFO_PHP or TIMELIB_TZINFO_ZONEINFO */
@@ -524,7 +1067,7 @@ timelib_tzinfo *timelib_parse_tzfile(const char *timezone, const timelib_tzdb *t
*error_code = TIMELIB_ERROR_NO_ERROR;
- if (seek_to_tz_position(&tzf, timezone, tzdb)) {
+ if (seek_to_tz_position(&tzf, timezone, &memmap, &maplen, tzdb)) {
tmp = timelib_tzinfo_ctor(timezone);
version = read_preamble(&tzf, tmp, &type);
@@ -540,11 +1044,36 @@ timelib_tzinfo *timelib_parse_tzfile(con
@@ -563,11 +1106,36 @@ timelib_tzinfo *timelib_parse_tzfile(const char *timezone, const timelib_tzdb *t
}
skip_posix_string(&tzf, tmp);
@ -655,3 +700,19 @@ diff -up php-8.0.0beta3/ext/date/lib/parse_tz.c.systzdata php-8.0.0beta3/ext/dat
} else {
*error_code = TIMELIB_ERROR_NO_SUCH_TIMEZONE;
tmp = NULL;
diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index 2d5cffb963..389f09f313 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -457,7 +457,11 @@ PHP_MINFO_FUNCTION(date)
php_info_print_table_row(2, "date/time support", "enabled");
php_info_print_table_row(2, "timelib version", TIMELIB_ASCII_VERSION);
php_info_print_table_row(2, "\"Olson\" Timezone Database Version", tzdb->version);
+#ifdef HAVE_SYSTEM_TZDATA
+ php_info_print_table_row(2, "Timezone Database", "system");
+#else
php_info_print_table_row(2, "Timezone Database", php_date_global_timezone_db_enabled ? "external" : "internal");
+#endif
php_info_print_table_row(2, "Default timezone", guess_timezone(tzdb));
php_info_print_table_end();

View File

@ -6,7 +6,7 @@
# Cannot load both php5 and php7 modules
<IfModule !mod_php5.c>
<IfModule prefork.c>
LoadModule php7_module modules/libphp7.so
LoadModule php7_module modules/libphp.so
</IfModule>
</IfModule>

View File

@ -20,13 +20,13 @@
%global with_firebird 0
%global with_imap 0
%global with_freetds 0
%global with_sodium 0
%global with_sodium 1
%global with_pspell 0
%global upver 8.0.0
%global upver 8.0.24
Name: php
Version: %{upver}%{?rcver:~%{rcver}}
Release: 8
Release: 1
Summary: PHP scripting language for creating dynamic web sites
License: PHP and Zend-2.0 and BSD and MIT and ASL 1.0 and NCSA
URL: http://www.php.net/
@ -50,22 +50,11 @@ Source52: 20-ffi.ini
Patch0: php-7.4.0-httpd.patch
Patch1: php-7.2.0-includedir.patch
Patch2: php-8.0.0-embed.patch
Patch3: php-8.0.0-systzdata-v19.patch
Patch3: php-8.0.10-systzdata-v21.patch
Patch4: php-7.4.0-phpize.patch
Patch5: php-7.4.0-ldap_r.patch
Patch6: php-8.0.0-phpinfo.patch
Patch7: php-7.4.0-datetests.patch
Patch8: backport-CVE-2020-7071-Fix-bug-77423.patch
Patch9: backport-0001-CVE-2020-7071.patch
Patch10: backport-0002-CVE-2020-7071.patch
Patch11: backport-0001-CVE-2021-21705.patch
Patch12: backport-0002-CVE-2021-21705.patch
Patch13: backport-CVE-2021-21704.patch
Patch14: backport-CVE-2021-21703.patch
Patch15: backport-CVE-2021-21707.patch
Patch16: backport-CVE-2021-21708-Fix-81708.patch
Patch17: backport-CVE-2022-31625.patch
Patch18: backport-CVE-2022-31626.patch
BuildRequires: bzip2-devel, curl-devel >= 7.9, httpd-devel >= 2.0.46-1, pam-devel, httpd-filesystem, nginx-filesystem
BuildRequires: libstdc++-devel, openssl-devel, sqlite-devel >= 3.6.0, zlib-devel, smtpdaemon, libedit-devel
@ -120,7 +109,6 @@ The php-dbg package contains the interactive PHP debugger.
Summary: PHP FastCGI Process Manager
BuildRequires: libacl-devel
Requires: php-common%{?_isa} = %{version}-%{release}
Requires(pre): /usr/sbin/useradd
BuildRequires: systemd-devel
%{?systemd_requires}
Requires(pre): httpd-filesystem
@ -591,9 +579,9 @@ ln -sf ../configure
--enable-rtld-now \
--cache-file=../config.cache --with-libdir=%{_lib} --with-config-file-path=%{_sysconfdir} \
--with-config-file-scan-dir=%{_sysconfdir}/php.d --disable-debug --with-pic --disable-rpath \
--without-pear --with-exec-dir=%{_bindir} --without-gdbm --with-openssl \
--with-system-ciphers --with-pcre-regex=%{_prefix} --with-zlib --with-layout=GNU --with-kerberos \
--with-libxml-dir=%{_prefix} --with-system-tzdata --with-mhash \
--without-pear --without-gdbm --with-openssl \
--with-system-ciphers --with-zlib --with-layout=GNU --with-kerberos \
--with-system-tzdata --with-mhash \
%if %{with_argon2}
--with-password-argon2 \
%endif
@ -662,7 +650,7 @@ build --libdir=%{_libdir}/php --enable-pcntl --enable-opcache --enable-phpdbg \
--enable-intl=shared --with-enchant=shared,%{_prefix}
popd
without_shared="--without-gd --disable-dom --disable-dba --without-unixODBC --disable-opcache \
without_shared="--disable-gd --disable-dom --disable-dba --without-unixODBC --disable-opcache \
--disable-opcache --disable-phpdbg --without-ffi \
--disable-xmlreader --disable-xmlwriter --without-sodium --without-sqlite3 --disable-phar --disable-fileinfo \
--without-pspell --without-curl --disable-posix --disable-xml --disable-simplexml --disable-exif \
@ -1094,6 +1082,9 @@ systemctl try-restart php-fpm.service >/dev/null 2>&1 || :
%{_mandir}/*
%changelog
* Sun Oct 9 2022 Funda Wang <fundawang@yeah.net> - 8.0.24-1
- New version 8.0.24
* Sat Jun 18 2022 Hugel <gengqihu1@h-partners.com> - 8.0.0-8
- Fix CVE-2022-31625 CVE-2022-31626