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))