Skip to content

Commit 952a05e

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

File tree

3 files changed

+150
-43
lines changed

3 files changed

+150
-43
lines changed

Lib/test/test_parser.py

Lines changed: 81 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 operator
@@ -386,6 +388,52 @@ def test_junk(self):
386388
# not even remotely valid:
387389
self.check_bad_tree((1, 2, 3), "<junk>")
388390

391+
def test_illegal_terminal(self):
392+
tree = \
393+
(257,
394+
(269,
395+
(270,
396+
(271,
397+
(277,
398+
(1,))),
399+
(4, ''))),
400+
(4, ''),
401+
(0, ''))
402+
self.check_bad_tree(tree, "too small items in terminal node")
403+
tree = \
404+
(257,
405+
(269,
406+
(270,
407+
(271,
408+
(277,
409+
(1, b'pass'))),
410+
(4, ''))),
411+
(4, ''),
412+
(0, ''))
413+
self.check_bad_tree(tree, "non-string second item in terminal node")
414+
tree = \
415+
(257,
416+
(269,
417+
(270,
418+
(271,
419+
(277,
420+
(1, 'pass', '0', 0))),
421+
(4, ''))),
422+
(4, ''),
423+
(0, ''))
424+
self.check_bad_tree(tree, "non-integer third item in terminal node")
425+
tree = \
426+
(257,
427+
(269,
428+
(270,
429+
(271,
430+
(277,
431+
(1, 'pass', 0, 0))),
432+
(4, ''))),
433+
(4, ''),
434+
(0, ''))
435+
self.check_bad_tree(tree, "too many items in terminal node")
436+
389437
def test_illegal_yield_1(self):
390438
# Illegal yield statement: def f(): return 1; yield 1
391439
tree = \
@@ -590,6 +638,24 @@ def test_missing_import_source(self):
590638
(4, ''), (0, ''))
591639
self.check_bad_tree(tree, "from import fred")
592640

641+
def test_illegal_encoding(self):
642+
# Illegal encoding declaration
643+
tree = \
644+
(338,
645+
(257, (0, '')))
646+
self.check_bad_tree(tree, "missed encoding")
647+
tree = \
648+
(338,
649+
(257, (0, '')),
650+
b'iso-8859-1')
651+
self.check_bad_tree(tree, "non-string encoding")
652+
tree = \
653+
(338,
654+
(257, (0, '')),
655+
'\udcff')
656+
with self.assertRaises(UnicodeEncodeError):
657+
parser.sequence2st(tree)
658+
593659

594660
class CompileTestCase(unittest.TestCase):
595661

@@ -728,6 +794,21 @@ def test_comparisons(self):
728794
self.assertRaises(TypeError, operator.lt, st1, 1815)
729795
self.assertRaises(TypeError, operator.gt, b'waterloo', st2)
730796

797+
def test_copy_pickle(self):
798+
sts = [
799+
parser.expr('2 + 3'),
800+
parser.suite('x = 2; y = x + 3'),
801+
parser.expr('list(x**3 for x in range(20))')
802+
]
803+
for st in sts:
804+
st_copy = copy.copy(st)
805+
self.assertEqual(st_copy.totuple(), st.totuple())
806+
st_copy = copy.deepcopy(st)
807+
self.assertEqual(st_copy.totuple(), st.totuple())
808+
for proto in range(pickle.HIGHEST_PROTOCOL+1):
809+
st_copy = pickle.loads(pickle.dumps(st, proto))
810+
self.assertEqual(st_copy.totuple(), st.totuple())
811+
731812
check_sizeof = support.check_sizeof
732813

733814
@support.cpython_only

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ Extension Modules
4949
Library
5050
-------
5151

52+
- bpo-30070: Fixed leaks and crashes in errors handling in the parser module.
53+
5254
- bpo-30061: Fixed crashes in IOBase methods __next__() and readlines() when
5355
readline() or __next__() respectively return non-sizeable object.
5456
Fixed possible other errors caused by not checking results of PyObject_Size(),

Modules/parsermodule.c

Lines changed: 67 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,9 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
763763
Py_ssize_t i;
764764
int err;
765765

766+
if (len < 0) {
767+
return NULL;
768+
}
766769
for (i = 1; i < len; ++i) {
767770
/* elem must always be a sequence, however simple */
768771
PyObject* elem = PySequence_GetItem(tuple, i);
@@ -783,7 +786,7 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
783786
if (type == -1 && PyErr_Occurred()) {
784787
Py_DECREF(temp);
785788
Py_DECREF(elem);
786-
return 0;
789+
return NULL;
787790
}
788791
}
789792
Py_DECREF(temp);
@@ -795,7 +798,7 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
795798
PyErr_SetObject(parser_error, err);
796799
Py_XDECREF(err);
797800
Py_XDECREF(elem);
798-
return (0);
801+
return NULL;
799802
}
800803
if (ISTERMINAL(type)) {
801804
Py_ssize_t len = PyObject_Size(elem);
@@ -804,58 +807,64 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
804807

805808
if ((len != 2) && (len != 3)) {
806809
err_string("terminal nodes must have 2 or 3 entries");
807-
return 0;
810+
Py_DECREF(elem);
811+
return NULL;
808812
}
809813
temp = PySequence_GetItem(elem, 1);
810-
if (temp == NULL)
811-
return 0;
814+
if (temp == NULL) {
815+
Py_DECREF(elem);
816+
return NULL;
817+
}
812818
if (!PyUnicode_Check(temp)) {
813819
PyErr_Format(parser_error,
814820
"second item in terminal node must be a string,"
815821
" found %s",
816822
Py_TYPE(temp)->tp_name);
817823
Py_DECREF(temp);
818824
Py_DECREF(elem);
819-
return 0;
825+
return NULL;
820826
}
821827
if (len == 3) {
822828
PyObject *o = PySequence_GetItem(elem, 2);
823-
if (o != NULL) {
824-
if (PyLong_Check(o)) {
825-
int num = _PyLong_AsInt(o);
826-
if (num == -1 && PyErr_Occurred()) {
827-
Py_DECREF(o);
828-
Py_DECREF(temp);
829-
Py_DECREF(elem);
830-
return 0;
831-
}
832-
*line_num = num;
833-
}
834-
else {
835-
PyErr_Format(parser_error,
836-
"third item in terminal node must be an"
837-
" integer, found %s",
838-
Py_TYPE(temp)->tp_name);
829+
if (o == NULL) {
830+
Py_DECREF(temp);
831+
Py_DECREF(elem);
832+
return NULL;
833+
}
834+
if (PyLong_Check(o)) {
835+
int num = _PyLong_AsInt(o);
836+
if (num == -1 && PyErr_Occurred()) {
839837
Py_DECREF(o);
840838
Py_DECREF(temp);
841839
Py_DECREF(elem);
842-
return 0;
840+
return NULL;
843841
}
842+
*line_num = num;
843+
}
844+
else {
845+
PyErr_Format(parser_error,
846+
"third item in terminal node must be an"
847+
" integer, found %s",
848+
Py_TYPE(temp)->tp_name);
844849
Py_DECREF(o);
850+
Py_DECREF(temp);
851+
Py_DECREF(elem);
852+
return NULL;
845853
}
854+
Py_DECREF(o);
846855
}
847856
temp_str = _PyUnicode_AsStringAndSize(temp, &len);
848857
if (temp_str == NULL) {
849858
Py_DECREF(temp);
850-
Py_XDECREF(elem);
851-
return 0;
859+
Py_DECREF(elem);
860+
return NULL;
852861
}
853862
strn = (char *)PyObject_MALLOC(len + 1);
854863
if (strn == NULL) {
855864
Py_DECREF(temp);
856-
Py_XDECREF(elem);
865+
Py_DECREF(elem);
857866
PyErr_NoMemory();
858-
return 0;
867+
return NULL;
859868
}
860869
(void) memcpy(strn, temp_str, len + 1);
861870
Py_DECREF(temp);
@@ -865,20 +874,21 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
865874
* It has to be one or the other; this is an error.
866875
* Raise an exception.
867876
*/
868-
PyObject *err = Py_BuildValue("os", elem, "unknown node type.");
877+
PyObject *err = Py_BuildValue("Os", elem, "unknown node type.");
869878
PyErr_SetObject(parser_error, err);
870879
Py_XDECREF(err);
871-
Py_XDECREF(elem);
872-
return (0);
880+
Py_DECREF(elem);
881+
return NULL;
873882
}
874883
err = PyNode_AddChild(root, type, strn, *line_num, 0);
875884
if (err == E_NOMEM) {
876-
Py_XDECREF(elem);
885+
Py_DECREF(elem);
877886
PyObject_FREE(strn);
878-
return (node *) PyErr_NoMemory();
887+
PyErr_NoMemory();
888+
return NULL;
879889
}
880890
if (err == E_OVERFLOW) {
881-
Py_XDECREF(elem);
891+
Py_DECREF(elem);
882892
PyObject_FREE(strn);
883893
PyErr_SetString(PyExc_ValueError,
884894
"unsupported number of child nodes");
@@ -889,14 +899,14 @@ build_node_children(PyObject *tuple, node *root, int *line_num)
889899
node* new_child = CHILD(root, i - 1);
890900

891901
if (new_child != build_node_children(elem, new_child, line_num)) {
892-
Py_XDECREF(elem);
893-
return (0);
902+
Py_DECREF(elem);
903+
return NULL;
894904
}
895905
}
896906
else if (type == NEWLINE) { /* It's true: we increment the */
897907
++(*line_num); /* line number *after* the newline! */
898908
}
899-
Py_XDECREF(elem);
909+
Py_DECREF(elem);
900910
}
901911
return root;
902912
}
@@ -931,10 +941,23 @@ build_node_tree(PyObject *tuple)
931941

932942
if (num == encoding_decl) {
933943
encoding = PySequence_GetItem(tuple, 2);
944+
if (encoding == NULL) {
945+
PyErr_SetString(parser_error, "missed encoding");
946+
return NULL;
947+
}
948+
if (!PyUnicode_Check(encoding)) {
949+
PyErr_Format(parser_error,
950+
"encoding must be a string, found %.200s",
951+
Py_TYPE(encoding)->tp_name);
952+
Py_DECREF(encoding);
953+
return NULL;
954+
}
934955
/* tuple isn't borrowed anymore here, need to DECREF */
935956
tuple = PySequence_GetSlice(tuple, 0, 2);
936-
if (tuple == NULL)
957+
if (tuple == NULL) {
958+
Py_DECREF(encoding);
937959
return NULL;
960+
}
938961
}
939962
res = PyNode_New(num);
940963
if (res != NULL) {
@@ -947,31 +970,33 @@ build_node_tree(PyObject *tuple)
947970
const char *temp;
948971
temp = _PyUnicode_AsStringAndSize(encoding, &len);
949972
if (temp == NULL) {
950-
Py_DECREF(res);
973+
PyNode_Free(res);
951974
Py_DECREF(encoding);
952975
Py_DECREF(tuple);
953976
return NULL;
954977
}
955978
res->n_str = (char *)PyObject_MALLOC(len + 1);
956979
if (res->n_str == NULL) {
957-
Py_DECREF(res);
980+
PyNode_Free(res);
958981
Py_DECREF(encoding);
959982
Py_DECREF(tuple);
960983
PyErr_NoMemory();
961984
return NULL;
962985
}
963986
(void) memcpy(res->n_str, temp, len + 1);
964-
Py_DECREF(encoding);
965-
Py_DECREF(tuple);
966987
}
967988
}
989+
if (encoding != NULL) {
990+
Py_DECREF(encoding);
991+
Py_DECREF(tuple);
992+
}
968993
}
969994
else {
970995
/* The tuple is illegal -- if the number is neither TERMINAL nor
971996
* NONTERMINAL, we can't use it. Not sure the implementation
972997
* allows this condition, but the API doesn't preclude it.
973998
*/
974-
PyObject *err = Py_BuildValue("os", tuple,
999+
PyObject *err = Py_BuildValue("Os", tuple,
9751000
"Illegal component tuple.");
9761001
PyErr_SetObject(parser_error, err);
9771002
Py_XDECREF(err);
@@ -3433,7 +3458,6 @@ parser__pickler(PyObject *self, PyObject *args)
34333458
result = Py_BuildValue("O(O)", pickle_constructor, tuple);
34343459
Py_DECREF(tuple);
34353460
}
3436-
Py_DECREF(empty_dict);
34373461
Py_DECREF(newargs);
34383462
}
34393463
finally:

0 commit comments

Comments
 (0)