Serhiy Storchaka added the comment:

Oh, sorry. I again forgot to upload a patch.

----------
keywords: +patch
Added file: http://bugs.python.org/file36800/issue11145.patch

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue11145>
_______________________________________
diff -r a3e18dd8f267 Lib/test/test_format.py
--- a/Lib/test/test_format.py   Sat Oct 04 15:04:41 2014 +0300
+++ b/Lib/test/test_format.py   Sat Oct 04 20:07:18 2014 +0300
@@ -299,6 +299,39 @@ class FormatTest(unittest.TestCase):
             else:
                 raise TestFailed, '"%*d"%(maxsize, -127) should fail'
 
+    def test_invalid_special_methods(self):
+        tests = []
+        for f in 'sriduoxXfge':
+            tests.append(('%' + f, 1, TypeError))
+        for r in ['', '-', 'L', '-L']:
+            for f in 'iduoxX':
+                tests.append(('%' + f, r, ValueError))
+        tests.append(('%o', 'abc', ValueError))
+        for r in ('abc', '0abc', '0x', '0xL'):
+            for f in 'xX':
+                tests.append(('%' + f, r, ValueError))
+
+        class X(long):
+            def __repr__(self):
+                return result
+            def __str__(self):
+                return result
+            def __oct__(self):
+                return result
+            def __hex__(self):
+                return result
+            def __float__(self):
+                return result
+        for fmt, result, exc in tests:
+            try:
+                fmt % X()
+            except exc:
+                pass
+            else:
+                self.fail('%s not raised for %r format of %r' %
+                          (exc.__name__, fmt, result))
+
+
 def test_main():
     test_support.run_unittest(FormatTest)
 
diff -r a3e18dd8f267 Objects/stringobject.c
--- a/Objects/stringobject.c    Sat Oct 04 15:04:41 2014 +0300
+++ b/Objects/stringobject.c    Sat Oct 04 20:07:18 2014 +0300
@@ -4015,17 +4015,22 @@ PyObject*
     Py_ssize_t llen;
     int numdigits;      /* len == numnondigits + numdigits */
     int numnondigits = 0;
+    const char *method;
+    int modify = (type == 'X');
 
     switch (type) {
     case 'd':
     case 'u':
+        method = "str";
         result = Py_TYPE(val)->tp_str(val);
         break;
     case 'o':
+        method = "oct";
         result = Py_TYPE(val)->tp_as_number->nb_oct(val);
         break;
     case 'x':
     case 'X':
+        method = "hex";
         numnondigits = 2;
         result = Py_TYPE(val)->tp_as_number->nb_hex(val);
         break;
@@ -4041,25 +4046,39 @@ PyObject*
         return NULL;
     }
 
-    /* To modify the string in-place, there can only be one reference. */
-    if (Py_REFCNT(result) != 1) {
-        PyErr_BadInternalCall();
-        return NULL;
-    }
-    llen = PyString_Size(result);
+    llen = PyString_GET_SIZE(result);
     if (llen > INT_MAX) {
         PyErr_SetString(PyExc_ValueError, "string too large in 
_PyString_FormatLong");
+        Py_DECREF(result);
         return NULL;
     }
     len = (int)llen;
-    if (buf[len-1] == 'L') {
+    if (len > 0 && buf[len-1] == 'L') {
         --len;
-        buf[len] = '\0';
+        if (len == 0)
+            goto error;
+        modify = 1;
+    }
+    /* To modify the string in-place, there can only be one reference. */
+    if (modify && (llen <= 1 || Py_REFCNT(result) != 1)) {
+        /* Do not use PyString_FromStringAndSize(buf, len) because it can
+         * return existing object if len <= 1. */
+        PyObject *r1 = PyString_FromStringAndSize(NULL, len);
+        if (!r1) {
+            Py_DECREF(result);
+            return NULL;
+        }
+        assert(len == 0 || Py_REFCNT(r1) == 1);
+        memcpy(PyString_AS_STRING(r1), buf, len);
+        Py_DECREF(result);
+        result = r1;
+        buf = PyString_AS_STRING(result);
     }
     sign = buf[0] == '-';
     numnondigits += sign;
     numdigits = len - numnondigits;
-    assert(numdigits > 0);
+    if (numdigits <= 0)
+        goto error;
 
     /* Get rid of base marker unless F_ALT */
     if ((flags & F_ALT) == 0) {
@@ -4067,7 +4086,8 @@ PyObject*
         int skipped = 0;
         switch (type) {
         case 'o':
-            assert(buf[sign] == '0');
+            if (buf[sign] != '0')
+                goto error;
             /* If 0 is only digit, leave it alone. */
             if (numdigits > 1) {
                 skipped = 1;
@@ -4076,8 +4096,9 @@ PyObject*
             break;
         case 'x':
         case 'X':
-            assert(buf[sign] == '0');
-            assert(buf[sign + 1] == 'x');
+            if (buf[sign] != '0' ||
+                buf[sign + 1] != 'x')
+                goto error;
             skipped = 2;
             numnondigits -= 2;
             break;
@@ -4126,6 +4147,12 @@ PyObject*
     *pbuf = buf;
     *plen = len;
     return result;
+
+error:
+    PyErr_Format(PyExc_ValueError, "%%%c format: invalid result of %s.__%s__",
+                 type, Py_TYPE(val)->tp_name, method);
+    Py_DECREF(result);
+    return NULL;
 }
 
 Py_LOCAL_INLINE(int)
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to