Skip to content

Commit 5b5fe43

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: Fix GH-20374: PHP with tidy and custom-tags pgsql: Fix memory leak when object init fails (#20387)
2 parents 1bf85d0 + b8e9f7c commit 5b5fe43

File tree

4 files changed

+207
-5
lines changed

4 files changed

+207
-5
lines changed

ext/pgsql/pgsql.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2076,6 +2076,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
20762076
ZVAL_COPY_VALUE(&dataset, return_value);
20772077
zend_result obj_initialized = object_init_ex(return_value, ce);
20782078
if (UNEXPECTED(obj_initialized == FAILURE)) {
2079+
zval_ptr_dtor(&dataset);
20792080
RETURN_THROWS();
20802081
}
20812082
if (!ce->default_properties_count && !ce->__set) {

ext/tidy/tests/018.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ tidy
77
$x = tidy_repair_string("<p>abra\0cadabra</p>",
88
array( 'show-body-only' => true,
99
'clean' => false,
10-
'newline' => "\n")
10+
'newline' => "LF")
1111
);
1212
var_dump($x);
1313
?>

ext/tidy/tests/gh20374.phpt

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
--TEST--
2+
GH-20374 (PHP with tidy and custom-tags)
3+
--EXTENSIONS--
4+
tidy
5+
--CREDITS--
6+
franck-paul
7+
--FILE--
8+
<?php
9+
10+
class MyStringable {
11+
public function __construct(private $ret) {}
12+
13+
public function __toString(): string {
14+
return $this->ret;
15+
}
16+
}
17+
18+
class MyThrowingStringable {
19+
public function __toString(): string {
20+
throw new Error('no');
21+
}
22+
}
23+
24+
$values = [
25+
'string blocklevel' => 'blocklevel',
26+
'int' => 1,
27+
'double overflow' => (string) (2.0**80.0),
28+
'numeric string int 1' => '1',
29+
'numeric string double 1.0' => '1.0',
30+
'false' => false,
31+
'true' => true,
32+
'NAN' => NAN,
33+
'INF' => INF,
34+
'object with numeric string int 0' => new MyStringable('0'),
35+
'object with string blocklevel' => new MyStringable('blocklevel'),
36+
'object with string empty' => new MyStringable('empty'),
37+
'object with exception' => new MyThrowingStringable,
38+
];
39+
40+
foreach ($values as $key => $value) {
41+
echo "--- $key ---\n";
42+
$str = '<custom-html-element>test</custom-html-element>';
43+
44+
$config = [
45+
'custom-tags' => $value,
46+
];
47+
48+
$tidy = new tidy();
49+
try {
50+
$tidy->parseString($str, $config, 'utf8');
51+
echo $tidy->value, "\n";
52+
} catch (Throwable $e) {
53+
echo $e::class, ": ", $e->getMessage(), "\n";
54+
}
55+
}
56+
57+
?>
58+
--EXPECTF--
59+
--- string blocklevel ---
60+
<html>
61+
<head>
62+
<title></title>
63+
</head>
64+
<body>
65+
<custom-html-element>test</custom-html-element>
66+
</body>
67+
</html>
68+
--- int ---
69+
<html>
70+
<head>
71+
<title></title>
72+
</head>
73+
<body>
74+
<custom-html-element>test</custom-html-element>
75+
</body>
76+
</html>
77+
--- double overflow ---
78+
79+
Warning: The float-string "1.2089258196146E+24" is not representable as an int, cast occurred in %s on line %d
80+
<html>
81+
<head>
82+
<title></title>
83+
</head>
84+
<body>
85+
test
86+
</body>
87+
</html>
88+
--- numeric string int 1 ---
89+
<html>
90+
<head>
91+
<title></title>
92+
</head>
93+
<body>
94+
<custom-html-element>test</custom-html-element>
95+
</body>
96+
</html>
97+
--- numeric string double 1.0 ---
98+
<html>
99+
<head>
100+
<title></title>
101+
</head>
102+
<body>
103+
<custom-html-element>test</custom-html-element>
104+
</body>
105+
</html>
106+
--- false ---
107+
<html>
108+
<head>
109+
<title></title>
110+
</head>
111+
<body>
112+
test
113+
</body>
114+
</html>
115+
--- true ---
116+
<html>
117+
<head>
118+
<title></title>
119+
</head>
120+
<body>
121+
<custom-html-element>test</custom-html-element>
122+
</body>
123+
</html>
124+
--- NAN ---
125+
126+
Warning: The float NAN is not representable as an int, cast occurred in %s on line %d
127+
<html>
128+
<head>
129+
<title></title>
130+
</head>
131+
<body>
132+
test
133+
</body>
134+
</html>
135+
--- INF ---
136+
137+
Warning: The float INF is not representable as an int, cast occurred in %s on line %d
138+
<html>
139+
<head>
140+
<title></title>
141+
</head>
142+
<body>
143+
test
144+
</body>
145+
</html>
146+
--- object with numeric string int 0 ---
147+
<html>
148+
<head>
149+
<title></title>
150+
</head>
151+
<body>
152+
test
153+
</body>
154+
</html>
155+
--- object with string blocklevel ---
156+
<html>
157+
<head>
158+
<title></title>
159+
</head>
160+
<body>
161+
<custom-html-element>test</custom-html-element>
162+
</body>
163+
</html>
164+
--- object with string empty ---
165+
<custom-html-element>
166+
<html>
167+
<head>
168+
<title></title>
169+
</head>
170+
<body>
171+
test
172+
</body>
173+
</html>
174+
--- object with exception ---
175+
Error: no

ext/tidy/tidy.c

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,7 @@ static bool php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value,
719719
{
720720
TidyOption opt = tidyGetOptionByName(doc, optname);
721721
zend_long lval;
722+
zend_string *tmp_str;
722723

723724
if (!opt) {
724725
zend_argument_value_error(arg, "Unknown Tidy configuration option \"%s\"", optname);
@@ -736,17 +737,42 @@ static bool php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value,
736737

737738
TidyOptionType type = tidyOptGetType(opt);
738739
if (type == TidyString) {
739-
zend_string *tmp_str;
740740
const zend_string *str = zval_get_tmp_string(value, &tmp_str);
741741
const bool result = tidyOptSetValue(doc, tidyOptGetId(opt), ZSTR_VAL(str));
742742
if (UNEXPECTED(!result)) {
743743
zend_argument_type_error(arg, "option \"%s\" does not accept \"%s\" as a value", optname, ZSTR_VAL(str));
744744
}
745745
zend_tmp_string_release(tmp_str);
746746
return result;
747-
} else if (type == TidyInteger) {
748-
lval = zval_get_long(value);
749-
return tidyOptSetInt(doc, tidyOptGetId(opt), lval);
747+
} else if (type == TidyInteger) { /* integer or enum */
748+
ZVAL_DEREF(value);
749+
/* Enum will correspond to a non-numeric string or object */
750+
if (Z_TYPE_P(value) == IS_STRING || Z_TYPE_P(value) == IS_OBJECT) {
751+
double dval;
752+
bool result;
753+
const zend_string *str = zval_try_get_tmp_string(value, &tmp_str);
754+
if (UNEXPECTED(!str)) {
755+
return false;
756+
}
757+
uint8_t type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &lval, &dval, true);
758+
if (type == IS_DOUBLE) {
759+
lval = zend_dval_to_lval_cap(dval, str);
760+
type = IS_LONG;
761+
}
762+
if (type == IS_LONG) {
763+
result = tidyOptSetInt(doc, tidyOptGetId(opt), lval);
764+
} else {
765+
result = tidyOptSetValue(doc, tidyOptGetId(opt), ZSTR_VAL(str));
766+
if (UNEXPECTED(!result)) {
767+
zend_argument_type_error(arg, "option \"%s\" does not accept \"%s\" as a value", optname, ZSTR_VAL(str));
768+
}
769+
}
770+
zend_tmp_string_release(tmp_str);
771+
return result;
772+
} else {
773+
lval = zval_get_long(value);
774+
return tidyOptSetInt(doc, tidyOptGetId(opt), lval);
775+
}
750776
} else {
751777
ZEND_ASSERT(type == TidyBoolean);
752778
lval = zval_get_long(value);

0 commit comments

Comments
 (0)