Skip to content

Commit 2a1bf06

Browse files
[2.7] [3.5] bpo-30070: Fixed leaks and crashes in errors handling in the parser module. (GH-1131). (GH-1185) (#1189)
(cherry picked from commit a79f4c2). (cherry picked from commit 952a05e)
1 parent 64aa4df commit 2a1bf06

File tree

3 files changed

+150
-32
lines changed

3 files changed

+150
-32
lines changed

Lib/test/test_parser.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import copy
12
import parser
3+
import pickle
24
import unittest
35
import sys
46
import struct
@@ -314,6 +316,52 @@ def test_junk(self):
314316
# not even remotely valid:
315317
self.check_bad_tree((1, 2, 3), "<junk>")
316318

319+
def test_illegal_terminal(self):
320+
tree = \
321+
(257,
322+
(267,
323+
(268,
324+
(269,
325+
(274,
326+
(1,))),
327+
(4, ''))),
328+
(4, ''),
329+
(0, ''))
330+
self.check_bad_tree(tree, "too small items in terminal node")
331+
tree = \
332+
(257,
333+
(267,
334+
(268,
335+
(269,
336+
(274,
337+
(1, u'pass'))),
338+
(4, ''))),
339+
(4, ''),
340+
(0, ''))
341+
self.check_bad_tree(tree, "non-string second item in terminal node")
342+
tree = \
343+
(257,
344+
(267,
345+
(268,
346+
(269,
347+
(274,
348+
(1, 'pass', '0', 0))),
349+
(4, ''))),
350+
(4, ''),
351+
(0, ''))
352+
self.check_bad_tree(tree, "non-integer third item in terminal node")
353+
tree = \
354+
(257,
355+
(267,
356+
(268,
357+
(269,
358+
(274,
359+
(1, 'pass', 0, 0))),
360+
(4, ''))),
361+
(4, ''),
362+
(0, ''))
363+
self.check_bad_tree(tree, "too many items in terminal node")
364+
317365
def test_illegal_yield_1(self):
318366
# Illegal yield statement: def f(): return 1; yield 1
319367
tree = \
@@ -541,6 +589,18 @@ def test_missing_import_source(self):
541589
(4, ''), (0, ''))
542590
self.check_bad_tree(tree, "from import a")
543591

592+
def test_illegal_encoding(self):
593+
# Illegal encoding declaration
594+
tree = \
595+
(339,
596+
(257, (0, '')))
597+
self.check_bad_tree(tree, "missed encoding")
598+
tree = \
599+
(339,
600+
(257, (0, '')),
601+
u'iso-8859-1')
602+
self.check_bad_tree(tree, "non-string encoding")
603+
544604

545605
class CompileTestCase(unittest.TestCase):
546606

@@ -602,6 +662,21 @@ def test_trigger_memory_error(self):
602662
class STObjectTestCase(unittest.TestCase):
603663
"""Test operations on ST objects themselves"""
604664

665+
def test_copy_pickle(self):
666+
sts = [
667+
parser.expr('2 + 3'),
668+
parser.suite('x = 2; y = x + 3'),
669+
parser.expr('list(x**3 for x in range(20))')
670+
]
671+
for st in sts:
672+
st_copy = copy.copy(st)
673+
self.assertEqual(st_copy.totuple(), st.totuple())
674+
st_copy = copy.deepcopy(st)
675+
self.assertEqual(st_copy.totuple(), st.totuple())
676+
for proto in range(pickle.HIGHEST_PROTOCOL+1):
677+
st_copy = pickle.loads(pickle.dumps(st, proto))
678+
self.assertEqual(st_copy.totuple(), st.totuple())
679+
605680
check_sizeof = support.check_sizeof
606681

607682
@support.cpython_only

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ Extension Modules
4242
Library
4343
-------
4444

45+
- bpo-30070: Fixed leaks and crashes in errors handling in the parser module.
46+
4547
- bpo-30061: Fixed crashes in IOBase methods next() and readlines() when
4648
readline() or next() respectively return non-sizeable object.
4749
Fixed possible other errors caused by not checking results of PyObject_Size(),

Modules/parsermodule.c

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,9 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
740740
Py_ssize_t i;
741741
int err;
742742

743+
if (len < 0) {
744+
return NULL;
745+
}
743746
for (i = 1; i < len; ++i) {
744747
/* elem must always be a sequence, however simple */
745748
PyObject* elem = PySequence_GetItem(tuple, i);
@@ -766,67 +769,83 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
766769
PyErr_SetObject(parser_error, err);
767770
Py_XDECREF(err);
768771
Py_XDECREF(elem);
769-
return (0);
772+
return NULL;
770773
}
771774
if (ISTERMINAL(type)) {
772775
Py_ssize_t len = PyObject_Size(elem);
773776
PyObject *temp;
774777

775778
if ((len != 2) && (len != 3)) {
776779
err_string("terminal nodes must have 2 or 3 entries");
777-
return 0;
780+
Py_DECREF(elem);
781+
return NULL;
778782
}
779783
temp = PySequence_GetItem(elem, 1);
780-
if (temp == NULL)
781-
return 0;
784+
if (temp == NULL) {
785+
Py_DECREF(elem);
786+
return NULL;
787+
}
782788
if (!PyString_Check(temp)) {
783789
PyErr_Format(parser_error,
784790
"second item in terminal node must be a string,"
785791
" found %s",
786792
Py_TYPE(temp)->tp_name);
787793
Py_DECREF(temp);
788-
return 0;
794+
Py_DECREF(elem);
795+
return NULL;
789796
}
790797
if (len == 3) {
791798
PyObject *o = PySequence_GetItem(elem, 2);
792-
if (o != NULL) {
793-
if (PyInt_Check(o))
794-
*line_num = PyInt_AS_LONG(o);
795-
else {
796-
PyErr_Format(parser_error,
797-
"third item in terminal node must be an"
798-
" integer, found %s",
799-
Py_TYPE(temp)->tp_name);
800-
Py_DECREF(o);
801-
Py_DECREF(temp);
802-
return 0;
803-
}
799+
if (o == NULL) {
800+
Py_DECREF(temp);
801+
Py_DECREF(elem);
802+
return NULL;
803+
}
804+
if (PyInt_Check(o))
805+
*line_num = PyInt_AS_LONG(o);
806+
else {
807+
PyErr_Format(parser_error,
808+
"third item in terminal node must be an"
809+
" integer, found %s",
810+
Py_TYPE(temp)->tp_name);
804811
Py_DECREF(o);
812+
Py_DECREF(temp);
813+
Py_DECREF(elem);
814+
return NULL;
805815
}
816+
Py_DECREF(o);
806817
}
807818
len = PyString_GET_SIZE(temp) + 1;
808819
strn = (char *)PyObject_MALLOC(len);
809-
if (strn != NULL)
810-
(void) memcpy(strn, PyString_AS_STRING(temp), len);
820+
if (strn == NULL) {
821+
Py_DECREF(temp);
822+
Py_DECREF(elem);
823+
PyErr_NoMemory();
824+
return NULL;
825+
}
826+
(void) memcpy(strn, PyString_AS_STRING(temp), len);
811827
Py_DECREF(temp);
812828
}
813829
else if (!ISNONTERMINAL(type)) {
814830
/*
815831
* It has to be one or the other; this is an error.
816832
* Raise an exception.
817833
*/
818-
PyObject *err = Py_BuildValue("os", elem, "unknown node type.");
834+
PyObject *err = Py_BuildValue("Os", elem, "unknown node type.");
819835
PyErr_SetObject(parser_error, err);
820836
Py_XDECREF(err);
821-
Py_XDECREF(elem);
822-
return (0);
837+
Py_DECREF(elem);
838+
return NULL;
823839
}
824840
err = PyNode_AddChild(root, type, strn, *line_num, 0);
825841
if (err == E_NOMEM) {
842+
Py_DECREF(elem);
826843
PyObject_FREE(strn);
827-
return (node *) PyErr_NoMemory();
844+
PyErr_NoMemory();
845+
return NULL;
828846
}
829847
if (err == E_OVERFLOW) {
848+
Py_DECREF(elem);
830849
PyObject_FREE(strn);
831850
PyErr_SetString(PyExc_ValueError,
832851
"unsupported number of child nodes");
@@ -837,14 +856,14 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
837856
node* new_child = CHILD(root, i - 1);
838857

839858
if (new_child != build_node_children(elem, new_child, line_num)) {
840-
Py_XDECREF(elem);
841-
return (0);
859+
Py_DECREF(elem);
860+
return NULL;
842861
}
843862
}
844863
else if (type == NEWLINE) { /* It's true: we increment the */
845864
++(*line_num); /* line number *after* the newline! */
846865
}
847-
Py_XDECREF(elem);
866+
Py_DECREF(elem);
848867
}
849868
return root;
850869
}
@@ -879,8 +898,23 @@ build_node_tree(PyObject *tuple)
879898

880899
if (num == encoding_decl) {
881900
encoding = PySequence_GetItem(tuple, 2);
901+
if (encoding == NULL) {
902+
PyErr_SetString(parser_error, "missed encoding");
903+
return NULL;
904+
}
905+
if (!PyString_Check(encoding)) {
906+
PyErr_Format(parser_error,
907+
"encoding must be a string, found %.200s",
908+
Py_TYPE(encoding)->tp_name);
909+
Py_DECREF(encoding);
910+
return NULL;
911+
}
882912
/* tuple isn't borrowed anymore here, need to DECREF */
883913
tuple = PySequence_GetSlice(tuple, 0, 2);
914+
if (tuple == NULL) {
915+
Py_DECREF(encoding);
916+
return NULL;
917+
}
884918
}
885919
res = PyNode_New(num);
886920
if (res != NULL) {
@@ -892,19 +926,27 @@ build_node_tree(PyObject *tuple)
892926
Py_ssize_t len;
893927
len = PyString_GET_SIZE(encoding) + 1;
894928
res->n_str = (char *)PyObject_MALLOC(len);
895-
if (res->n_str != NULL)
896-
(void) memcpy(res->n_str, PyString_AS_STRING(encoding), len);
897-
Py_DECREF(encoding);
898-
Py_DECREF(tuple);
929+
if (res->n_str == NULL) {
930+
PyNode_Free(res);
931+
Py_DECREF(encoding);
932+
Py_DECREF(tuple);
933+
PyErr_NoMemory();
934+
return NULL;
935+
}
936+
(void) memcpy(res->n_str, PyString_AS_STRING(encoding), len);
899937
}
900938
}
939+
if (encoding != NULL) {
940+
Py_DECREF(encoding);
941+
Py_DECREF(tuple);
942+
}
901943
}
902944
else {
903945
/* The tuple is illegal -- if the number is neither TERMINAL nor
904946
* NONTERMINAL, we can't use it. Not sure the implementation
905947
* allows this condition, but the API doesn't preclude it.
906948
*/
907-
PyObject *err = Py_BuildValue("os", tuple,
949+
PyObject *err = Py_BuildValue("Os", tuple,
908950
"Illegal component tuple.");
909951
PyErr_SetObject(parser_error, err);
910952
Py_XDECREF(err);
@@ -3371,7 +3413,6 @@ parser__pickler(PyObject *self, PyObject *args)
33713413
result = Py_BuildValue("O(O)", pickle_constructor, tuple);
33723414
Py_DECREF(tuple);
33733415
}
3374-
Py_DECREF(empty_dict);
33753416
Py_DECREF(newargs);
33763417
}
33773418
finally:

0 commit comments

Comments
 (0)