Fix Bug #114293:
Strings are unpickled by calling eval on the string's repr. This
change makes pickle work like cPickle; it checks if the pickled
string is safe to eval and raises ValueError if it is not.
test suite modifications:
Verify that pickle catches a variety of insecure string pickles
Make test_pickle and test_cpickle use exactly the same test suite
Add test for pickling recursive object
diff --git a/Lib/pickle.py b/Lib/pickle.py
index 6c1bcbe..128a627 100644
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -577,10 +577,50 @@
dispatch[BINFLOAT] = load_binfloat
def load_string(self):
- self.append(eval(self.readline()[:-1],
+ rep = self.readline()[:-1]
+ if not self._is_string_secure(rep):
+ raise ValueError, "insecure string pickle"
+ self.append(eval(rep,
{'__builtins__': {}})) # Let's be careful
dispatch[STRING] = load_string
+ def _is_string_secure(self, s):
+ """Return true if s contains a string that is safe to eval
+
+ The definition of secure string is based on the implementation
+ in cPickle. s is secure as long as it only contains a quoted
+ string and optional trailing whitespace.
+ """
+ q = s[0]
+ if q not in ("'", '"'):
+ return 0
+ # find the closing quote
+ offset = 1
+ i = None
+ while 1:
+ try:
+ i = s.index(q, offset)
+ except ValueError:
+ # if there is an error the first time, there is no
+ # close quote
+ if offset == 1:
+ return 0
+ if s[i-1] != '\\':
+ break
+ # check to see if this one is escaped
+ nslash = 0
+ j = i - 1
+ while j >= offset and s[j] == '\\':
+ j = j - 1
+ nslash = nslash + 1
+ if nslash % 2 == 0:
+ break
+ offset = i + 1
+ for c in s[i+1:]:
+ if ord(c) > 32:
+ return 0
+ return 1
+
def load_binstring(self):
len = mloads('i' + self.read(4))
self.append(self.read(len))