Skip to content

Commit 9164dc1

Browse files
committed
Fix #72714: _xml_startElementHandler() segmentation fault
The issue is caused by an integer overflow when the `long` passed as XML_OPTION_SKIP_TAGSTART is assigned to `xml_parser::toffset` which is declared as `int`. We can simply work around this issue, by clipping resulting negative values to 0 (and raising a notice in this case), because the reasonable range for this value is certainly catered to by positive `int`s. However, there still remains the issue that `xml_parser::toffset` is later added to `char *`s, which can cause OOB reads, so we make sure that the upper bound never exceeds the strlen(). We eschew optimizing `SKIP_TAGSTART` wrt. to the potentially duplicate strlen() call, because that code path is unexpected anyway.
1 parent f682193 commit 9164dc1

File tree

3 files changed

+52
-8
lines changed

3 files changed

+52
-8
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ PHP NEWS
3636

3737
- XML:
3838
. Fixed bug #72085 (SEGV on unknown address zif_xml_parse). (cmb)
39+
. Fixed bug #72714 (_xml_startElementHandler() segmentation fault). (cmb)
3940

4041
- ZIP:
4142
. Fixed bug #68302 (impossible to compile php with zip support). (cmb)

ext/xml/tests/bug72714.phpt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
Bug #72714 (_xml_startElementHandler() segmentation fault)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('xml')) die('skip xml extension not available');
6+
?>
7+
--FILE--
8+
<?php
9+
function startElement($parser, $name, $attribs) {
10+
var_dump($name);
11+
}
12+
13+
function endElement($parser, $name) {}
14+
15+
function parse($tagstart) {
16+
$xml = '<ns1:total>867</ns1:total>';
17+
18+
$xml_parser = xml_parser_create();
19+
xml_set_element_handler($xml_parser, 'startElement', 'endElement');
20+
21+
xml_parser_set_option($xml_parser, XML_OPTION_SKIP_TAGSTART, $tagstart);
22+
xml_parse($xml_parser, $xml);
23+
24+
xml_parser_free($xml_parser);
25+
}
26+
27+
parse(3015809298423721);
28+
parse(20);
29+
?>
30+
===DONE===
31+
--EXPECTF--
32+
Notice: xml_parser_set_option(): tagstart ignored in %s%ebug72714.php on line %d
33+
string(9) "NS1:TOTAL"
34+
string(0) ""
35+
===DONE===

ext/xml/xml.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ ZEND_GET_MODULE(xml)
6666
#endif /* COMPILE_DL_XML */
6767
/* }}} */
6868

69+
70+
#define SKIP_TAGSTART(str) ((str) + (parser->toffset > strlen(str) ? strlen(str) : + parser->toffset))
71+
72+
6973
/* {{{ function prototypes */
7074
PHP_MINIT_FUNCTION(xml);
7175
PHP_MINFO_FUNCTION(xml);
@@ -785,7 +789,7 @@ void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Ch
785789

786790
if (parser->startElementHandler) {
787791
args[0] = _xml_resource_zval(parser->index);
788-
args[1] = _xml_string_zval(((char *) tag_name) + parser->toffset);
792+
args[1] = _xml_string_zval(SKIP_TAGSTART((char *) tag_name));
789793
MAKE_STD_ZVAL(args[2]);
790794
array_init(args[2]);
791795

@@ -816,9 +820,9 @@ void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Ch
816820
array_init(tag);
817821
array_init(atr);
818822

819-
_xml_add_to_info(parser,((char *) tag_name) + parser->toffset);
823+
_xml_add_to_info(parser,SKIP_TAGSTART((char *) tag_name));
820824

821-
add_assoc_string(tag,"tag",((char *) tag_name) + parser->toffset,1); /* cast to avoid gcc-warning */
825+
add_assoc_string(tag,"tag",SKIP_TAGSTART((char *) tag_name),1);
822826
add_assoc_string(tag,"type","open",1);
823827
add_assoc_long(tag,"level",parser->level);
824828

@@ -870,7 +874,7 @@ void _xml_endElementHandler(void *userData, const XML_Char *name)
870874

871875
if (parser->endElementHandler) {
872876
args[0] = _xml_resource_zval(parser->index);
873-
args[1] = _xml_string_zval(((char *) tag_name) + parser->toffset);
877+
args[1] = _xml_string_zval(SKIP_TAGSTART((char *) tag_name));
874878

875879
if ((retval = xml_call_handler(parser, parser->endElementHandler, parser->endElementPtr, 2, args))) {
876880
zval_ptr_dtor(&retval);
@@ -887,9 +891,9 @@ void _xml_endElementHandler(void *userData, const XML_Char *name)
887891

888892
array_init(tag);
889893

890-
_xml_add_to_info(parser,((char *) tag_name) + parser->toffset);
894+
_xml_add_to_info(parser,SKIP_TAGSTART((char *) tag_name));
891895

892-
add_assoc_string(tag,"tag",((char *) tag_name) + parser->toffset,1); /* cast to avoid gcc-warning */
896+
add_assoc_string(tag,"tag",SKIP_TAGSTART((char *) tag_name),1);
893897
add_assoc_string(tag,"type","close",1);
894898
add_assoc_long(tag,"level",parser->level);
895899

@@ -990,9 +994,9 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len)
990994

991995
array_init(tag);
992996

993-
_xml_add_to_info(parser,parser->ltags[parser->level-1] + parser->toffset);
997+
_xml_add_to_info(parser,SKIP_TAGSTART(parser->ltags[parser->level-1]));
994998

995-
add_assoc_string(tag,"tag",parser->ltags[parser->level-1] + parser->toffset,1);
999+
add_assoc_string(tag,"tag",SKIP_TAGSTART(parser->ltags[parser->level-1]),1);
9961000
add_assoc_string(tag,"value",decoded_value,0);
9971001
add_assoc_string(tag,"type","cdata",1);
9981002
add_assoc_long(tag,"level",parser->level);
@@ -1633,6 +1637,10 @@ PHP_FUNCTION(xml_parser_set_option)
16331637
case PHP_XML_OPTION_SKIP_TAGSTART:
16341638
convert_to_long_ex(val);
16351639
parser->toffset = Z_LVAL_PP(val);
1640+
if (parser->toffset < 0) {
1641+
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "tagstart ignored");
1642+
parser->toffset = 0;
1643+
}
16361644
break;
16371645
case PHP_XML_OPTION_SKIP_WHITE:
16381646
convert_to_long_ex(val);

0 commit comments

Comments
 (0)