Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8a8da79

Browse files
committed
Patch #505705: Remove eval in pickle and cPickle.
1 parent cffac66 commit 8a8da79

8 files changed

Lines changed: 267 additions & 139 deletions

File tree

Include/stringobject.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,17 @@ PyAPI_FUNC(PyObject *) PyString_FromFormat(const char*, ...)
5353
__attribute__((format(printf, 1, 2)));
5454
PyAPI_FUNC(int) PyString_Size(PyObject *);
5555
PyAPI_FUNC(char *) PyString_AsString(PyObject *);
56+
PyAPI_FUNC(PyObject *) PyString_Repr(PyObject *, int);
5657
PyAPI_FUNC(void) PyString_Concat(PyObject **, PyObject *);
5758
PyAPI_FUNC(void) PyString_ConcatAndDel(PyObject **, PyObject *);
5859
PyAPI_FUNC(int) _PyString_Resize(PyObject **, int);
5960
PyAPI_FUNC(int) _PyString_Eq(PyObject *, PyObject*);
6061
PyAPI_FUNC(PyObject *) PyString_Format(PyObject *, PyObject *);
6162
PyAPI_FUNC(PyObject *) _PyString_FormatLong(PyObject*, int, int,
6263
int, char**, int*);
64+
extern DL_IMPORT(PyObject *) PyString_DecodeEscape(const char *, int,
65+
const char *, int,
66+
const char *);
6367

6468
PyAPI_FUNC(void) PyString_InternInPlace(PyObject **);
6569
PyAPI_FUNC(PyObject *) PyString_InternFromString(const char *);

Lib/encodings/string_escape.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# -*- coding: iso-8859-1 -*-
2+
""" Python 'escape' Codec
3+
4+
5+
Written by Martin v. Löwis ([email protected]).
6+
7+
"""
8+
import codecs
9+
10+
class Codec(codecs.Codec):
11+
12+
encode = codecs.escape_encode
13+
decode = codecs.escape_decode
14+
15+
class StreamWriter(Codec,codecs.StreamWriter):
16+
pass
17+
18+
class StreamReader(Codec,codecs.StreamReader):
19+
pass
20+
21+
def getregentry():
22+
23+
return (Codec.encode,Codec.decode,StreamReader,StreamWriter)

Lib/pickle.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ def __init__(self, value):
126126
__all__.extend([x for x in dir() if re.match("[A-Z][A-Z0-9_]+$",x)])
127127
del x
128128

129+
_quotes = ["'", '"']
130+
129131
class Pickler:
130132

131133
def __init__(self, file, bin = 0):
@@ -740,10 +742,15 @@ def load_binfloat(self, unpack=struct.unpack):
740742

741743
def load_string(self):
742744
rep = self.readline()[:-1]
743-
if not self._is_string_secure(rep):
745+
for q in _quotes:
746+
if rep.startswith(q):
747+
if not rep.endswith(q):
748+
raise ValueError, "insecure string pickle"
749+
rep = rep[len(q):-len(q)]
750+
break
751+
else:
744752
raise ValueError, "insecure string pickle"
745-
self.append(eval(rep,
746-
{'__builtins__': {}})) # Let's be careful
753+
self.append(rep.decode("string-escape"))
747754
dispatch[STRING] = load_string
748755

749756
def _is_string_secure(self, s):

Lib/test/pickletester.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,13 @@ def test_garyp(self):
195195

196196
def test_insecure_strings(self):
197197
insecure = ["abc", "2 + 2", # not quoted
198-
"'abc' + 'def'", # not a single quoted string
198+
#"'abc' + 'def'", # not a single quoted string
199199
"'abc", # quote is not closed
200200
"'abc\"", # open quote and close quote don't match
201201
"'abc' ?", # junk after close quote
202202
# some tests of the quoting rules
203-
"'abc\"\''",
204-
"'\\\\a\'\'\'\\\'\\\\\''",
203+
#"'abc\"\''",
204+
#"'\\\\a\'\'\'\\\'\\\\\''",
205205
]
206206
for s in insecure:
207207
buf = "S" + s + "\012p0\012."

Modules/_codecsmodule.c

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ PyObject *codeclookup(PyObject *self, PyObject *args)
7171
return NULL;
7272
}
7373

74-
#ifdef Py_USING_UNICODE
7574
/* --- Helpers ------------------------------------------------------------ */
7675

7776
static
@@ -97,6 +96,49 @@ PyObject *codec_tuple(PyObject *unicode,
9796
return v;
9897
}
9998

99+
/* --- String codecs ------------------------------------------------------ */
100+
static PyObject *
101+
escape_decode(PyObject *self,
102+
PyObject *args)
103+
{
104+
const char *errors = NULL;
105+
const char *data;
106+
int size;
107+
108+
if (!PyArg_ParseTuple(args, "s#|z:escape_decode",
109+
&data, &size, &errors))
110+
return NULL;
111+
return codec_tuple(PyString_DecodeEscape(data, size, errors, 0, NULL),
112+
size);
113+
}
114+
115+
static PyObject *
116+
escape_encode(PyObject *self,
117+
PyObject *args)
118+
{
119+
PyObject *str;
120+
const char *errors = NULL;
121+
char *buf;
122+
int len;
123+
124+
if (!PyArg_ParseTuple(args, "O!|z:escape_encode",
125+
&PyString_Type, &str, &errors))
126+
return NULL;
127+
128+
str = PyString_Repr(str, 0);
129+
if (!str)
130+
return NULL;
131+
132+
/* The string will be quoted. Unquote, similar to unicode-escape. */
133+
buf = PyString_AS_STRING (str);
134+
len = PyString_GET_SIZE (str);
135+
memmove(buf, buf+1, len-2);
136+
_PyString_Resize(&str, len-2);
137+
138+
return codec_tuple(str, PyString_Size(str));
139+
}
140+
141+
#ifdef Py_USING_UNICODE
100142
/* --- Decoder ------------------------------------------------------------ */
101143

102144
static PyObject *
@@ -669,6 +711,8 @@ mbcs_encode(PyObject *self,
669711
static PyMethodDef _codecs_functions[] = {
670712
{"register", codecregister, METH_VARARGS},
671713
{"lookup", codeclookup, METH_VARARGS},
714+
{"escape_encode", escape_encode, METH_VARARGS},
715+
{"escape_decode", escape_decode, METH_VARARGS},
672716
#ifdef Py_USING_UNICODE
673717
{"utf_8_encode", utf_8_encode, METH_VARARGS},
674718
{"utf_8_decode", utf_8_decode, METH_VARARGS},

Modules/cPickle.c

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2864,46 +2864,35 @@ static int
28642864
load_string(Unpicklerobject *self)
28652865
{
28662866
PyObject *str = 0;
2867-
int len, res = -1, nslash;
2868-
char *s, q, *p;
2869-
2870-
static PyObject *eval_dict = 0;
2867+
int len, res = -1;
2868+
char *s, *p;
28712869

28722870
if ((len = (*self->readline_func)(self, &s)) < 0) return -1;
28732871
if (len < 2) return bad_readline();
28742872
if (!( s=pystrndup(s,len))) return -1;
28752873

2876-
/* Check for unquoted quotes (evil strings) */
2877-
q=*s;
2878-
if (q != '"' && q != '\'') goto insecure;
2879-
for (p=s+1, nslash=0; *p; p++) {
2880-
if (*p==q && nslash%2==0) break;
2881-
if (*p=='\\') nslash++;
2882-
else nslash=0;
2883-
}
2884-
if (*p == q) {
2885-
for (p++; *p; p++)
2886-
if (*(unsigned char *)p > ' ')
2887-
goto insecure;
2888-
}
2889-
else
2874+
2875+
/* Strip outermost quotes */
2876+
while (s[len-1] <= ' ')
2877+
len--;
2878+
if(s[0]=='"' && s[len-1]=='"'){
2879+
s[len-1] = '\0';
2880+
p = s + 1 ;
2881+
len -= 2;
2882+
} else if(s[0]=='\'' && s[len-1]=='\''){
2883+
s[len-1] = '\0';
2884+
p = s + 1 ;
2885+
len -= 2;
2886+
} else
28902887
goto insecure;
28912888
/********************************************/
28922889

2893-
if (!( eval_dict ))
2894-
if (!( eval_dict = Py_BuildValue("{s{}}", "__builtins__")))
2895-
goto finally;
2896-
2897-
if (!( str = PyRun_String(s, Py_eval_input, eval_dict, eval_dict)))
2898-
goto finally;
2899-
2900-
free(s);
2901-
PDATA_PUSH(self->stack, str, -1);
2902-
return 0;
2903-
2904-
finally:
2890+
str = PyString_DecodeEscape(p, len, NULL, 0, NULL);
2891+
if (str) {
2892+
PDATA_PUSH(self->stack, str, -1);
2893+
res = 0;
2894+
}
29052895
free(s);
2906-
29072896
return res;
29082897

29092898
insecure:

0 commit comments

Comments
 (0)