[issue9647] os.confstr() does not handle value changing length between calls

David Watson report at bugs.python.org
Fri Dec 12 20:41:50 CET 2014


David Watson added the comment:

Here are the alternative patches to allow more than two calls to
confstr().  One patch set just keeps reallocating the buffer
until it's big enough, while the other makes a limited number of
attempts (in this case 20) before raising RuntimeError.

----------
keywords: +patch
Added file: http://bugs.python.org/file37428/confstr-realloc-endless-2.7.diff
Added file: http://bugs.python.org/file37429/confstr-realloc-endless-3.4.diff
Added file: http://bugs.python.org/file37430/confstr-realloc-endless-3.5.diff
Added file: http://bugs.python.org/file37431/confstr-realloc-limited-2.7.diff
Added file: http://bugs.python.org/file37432/confstr-realloc-limited-3.4.diff
Added file: http://bugs.python.org/file37433/confstr-realloc-limited-3.5.diff

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue9647>
_______________________________________
-------------- next part --------------
# HG changeset patch
# Parent 1edff7001f589dffaf8de4aebcb65a1f1d7deb5c

diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -7936,12 +7936,24 @@ posix_confstr(PyObject *self, PyObject *
     PyObject *result = NULL;
     int name;
     char buffer[256];
+    char *recvbuf = buffer;
+    char *allocated = NULL;
+    size_t buflen = sizeof(buffer);
+    size_t len;
 
     if (PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name)) {
-    int len;
-
-    errno = 0;
-    len = confstr(name, buffer, sizeof(buffer));
+
+    /* Reset errno before each call as we may need to check it afterwards */
+    while (errno = 0, (len = confstr(name, recvbuf, buflen)) > buflen) {
+        recvbuf = PyMem_Realloc(allocated, len);
+        if (recvbuf == NULL) {
+            PyErr_NoMemory();
+            goto finally;
+        }
+        allocated = recvbuf;
+        buflen = len;
+    }
+
     if (len == 0) {
         if (errno) {
         posix_error();
@@ -7952,15 +7964,11 @@ posix_confstr(PyObject *self, PyObject *
         }
     }
     else {
-        if ((unsigned int)len >= sizeof(buffer)) {
-        result = PyString_FromStringAndSize(NULL, len-1);
-        if (result != NULL)
-            confstr(name, PyString_AS_STRING(result), len);
-        }
-        else
-        result = PyString_FromStringAndSize(buffer, len-1);
-    }
-    }
+        result = PyString_FromStringAndSize(recvbuf, len-1);
+    }
+    }
+finally:
+    PyMem_Free(allocated);
     return result;
 }
 #endif
-------------- next part --------------
# HG changeset patch
# Parent aba5f771f5ec9e4004c0375ab4302f246a04bae7

diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -9727,33 +9727,38 @@ posix_confstr(PyObject *self, PyObject *
     PyObject *result = NULL;
     int name;
     char buffer[255];
+    char *recvbuf = buffer;
+    char *allocated = NULL;
+    size_t buflen = sizeof(buffer);
     size_t len;
 
     if (!PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name))
         return NULL;
 
-    errno = 0;
-    len = confstr(name, buffer, sizeof(buffer));
+    /* Reset errno before each call as we may need to check it afterwards */
+    while (errno = 0, (len = confstr(name, recvbuf, buflen)) > buflen) {
+        recvbuf = PyMem_Realloc(allocated, len);
+        if (recvbuf == NULL) {
+            PyErr_NoMemory();
+            goto finally;
+        }
+        allocated = recvbuf;
+        buflen = len;
+    }
+
     if (len == 0) {
         if (errno) {
             posix_error();
-            return NULL;
         }
         else {
-            Py_RETURN_NONE;
-        }
-    }
-
-    if (len >= sizeof(buffer)) {
-        char *buf = PyMem_Malloc(len);
-        if (buf == NULL)
-            return PyErr_NoMemory();
-        confstr(name, buf, len);
-        result = PyUnicode_DecodeFSDefaultAndSize(buf, len-1);
-        PyMem_Free(buf);
+            result = Py_None;
+            Py_INCREF(Py_None);
+        }
     }
     else
-        result = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1);
+        result = PyUnicode_DecodeFSDefaultAndSize(recvbuf, len-1);
+finally:
+    PyMem_Free(allocated);
     return result;
 }
 #endif
-------------- next part --------------
# HG changeset patch
# Parent 5754f069b12342acb69dff739782416092522748

diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -14372,32 +14372,35 @@ os_confstr_impl(PyModuleDef *module, int
 {
     PyObject *result = NULL;
     char buffer[255];
+    char *recvbuf = buffer;
+    char *allocated = NULL;
+    size_t buflen = sizeof(buffer);
     size_t len;
 
-    errno = 0;
-    len = confstr(name, buffer, sizeof(buffer));
+    /* Reset errno before each call as we may need to check it afterwards */
+    while (errno = 0, (len = confstr(name, recvbuf, buflen)) > buflen) {
+        recvbuf = PyMem_Realloc(allocated, len);
+        if (recvbuf == NULL) {
+            PyErr_NoMemory();
+            goto finally;
+        }
+        allocated = recvbuf;
+        buflen = len;
+    }
+
     if (len == 0) {
         if (errno) {
             posix_error();
-            return NULL;
         }
         else {
-            Py_RETURN_NONE;
-        }
-    }
-
-    if (len >= sizeof(buffer)) {
-        size_t len2;
-        char *buf = PyMem_Malloc(len);
-        if (buf == NULL)
-            return PyErr_NoMemory();
-        len2 = confstr(name, buf, len);
-        assert(len == len2);
-        result = PyUnicode_DecodeFSDefaultAndSize(buf, len-1);
-        PyMem_Free(buf);
-    }
-    else
-        result = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1);
+            result = Py_None;
+            Py_INCREF(Py_None);
+        }
+    }
+    else
+        result = PyUnicode_DecodeFSDefaultAndSize(recvbuf, len-1);
+finally:
+    PyMem_Free(allocated);
     return result;
 }
 #endif /* HAVE_CONFSTR */
-------------- next part --------------
# HG changeset patch
# Parent 1edff7001f589dffaf8de4aebcb65a1f1d7deb5c

diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -7936,12 +7936,32 @@ posix_confstr(PyObject *self, PyObject *
     PyObject *result = NULL;
     int name;
     char buffer[256];
+    char *recvbuf = buffer;
+    char *allocated = NULL;
+    size_t buflen = sizeof(buffer);
+    size_t len;
+    const int max_attempts = 20;
+    int attempts_remaining = max_attempts;
 
     if (PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name)) {
-    int len;
-
-    errno = 0;
-    len = confstr(name, buffer, sizeof(buffer));
+
+    /* Reset errno before each call as we may need to check it afterwards */
+    while (errno = 0, (len = confstr(name, recvbuf, buflen)) > buflen) {
+        if (--attempts_remaining <= 0) {
+            PyErr_Format(PyExc_RuntimeError,
+                         "confstr() made %d consecutive requests for a larger "
+                         "buffer; giving up", max_attempts);
+            goto finally;
+        }
+        recvbuf = PyMem_Realloc(allocated, len);
+        if (recvbuf == NULL) {
+            PyErr_NoMemory();
+            goto finally;
+        }
+        allocated = recvbuf;
+        buflen = len;
+    }
+
     if (len == 0) {
         if (errno) {
         posix_error();
@@ -7952,15 +7972,11 @@ posix_confstr(PyObject *self, PyObject *
         }
     }
     else {
-        if ((unsigned int)len >= sizeof(buffer)) {
-        result = PyString_FromStringAndSize(NULL, len-1);
-        if (result != NULL)
-            confstr(name, PyString_AS_STRING(result), len);
-        }
-        else
-        result = PyString_FromStringAndSize(buffer, len-1);
-    }
-    }
+        result = PyString_FromStringAndSize(recvbuf, len-1);
+    }
+    }
+finally:
+    PyMem_Free(allocated);
     return result;
 }
 #endif
-------------- next part --------------
# HG changeset patch
# Parent aba5f771f5ec9e4004c0375ab4302f246a04bae7

diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -9727,33 +9727,46 @@ posix_confstr(PyObject *self, PyObject *
     PyObject *result = NULL;
     int name;
     char buffer[255];
+    char *recvbuf = buffer;
+    char *allocated = NULL;
+    size_t buflen = sizeof(buffer);
     size_t len;
+    const int max_attempts = 20;
+    int attempts_remaining = max_attempts;
 
     if (!PyArg_ParseTuple(args, "O&:confstr", conv_confstr_confname, &name))
         return NULL;
 
-    errno = 0;
-    len = confstr(name, buffer, sizeof(buffer));
+    /* Reset errno before each call as we may need to check it afterwards */
+    while (errno = 0, (len = confstr(name, recvbuf, buflen)) > buflen) {
+        if (--attempts_remaining <= 0) {
+            PyErr_Format(PyExc_RuntimeError,
+                         "confstr() made %d consecutive requests for a larger "
+                         "buffer; giving up", max_attempts);
+            goto finally;
+        }
+        recvbuf = PyMem_Realloc(allocated, len);
+        if (recvbuf == NULL) {
+            PyErr_NoMemory();
+            goto finally;
+        }
+        allocated = recvbuf;
+        buflen = len;
+    }
+
     if (len == 0) {
         if (errno) {
             posix_error();
-            return NULL;
         }
         else {
-            Py_RETURN_NONE;
-        }
-    }
-
-    if (len >= sizeof(buffer)) {
-        char *buf = PyMem_Malloc(len);
-        if (buf == NULL)
-            return PyErr_NoMemory();
-        confstr(name, buf, len);
-        result = PyUnicode_DecodeFSDefaultAndSize(buf, len-1);
-        PyMem_Free(buf);
+            result = Py_None;
+            Py_INCREF(Py_None);
+        }
     }
     else
-        result = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1);
+        result = PyUnicode_DecodeFSDefaultAndSize(recvbuf, len-1);
+finally:
+    PyMem_Free(allocated);
     return result;
 }
 #endif
-------------- next part --------------
# HG changeset patch
# Parent 5754f069b12342acb69dff739782416092522748

diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -14372,32 +14372,43 @@ os_confstr_impl(PyModuleDef *module, int
 {
     PyObject *result = NULL;
     char buffer[255];
+    char *recvbuf = buffer;
+    char *allocated = NULL;
+    size_t buflen = sizeof(buffer);
     size_t len;
-
-    errno = 0;
-    len = confstr(name, buffer, sizeof(buffer));
+    const int max_attempts = 20;
+    int attempts_remaining = max_attempts;
+
+    /* Reset errno before each call as we may need to check it afterwards */
+    while (errno = 0, (len = confstr(name, recvbuf, buflen)) > buflen) {
+        if (--attempts_remaining <= 0) {
+            PyErr_Format(PyExc_RuntimeError,
+                         "confstr() made %d consecutive requests for a larger "
+                         "buffer; giving up", max_attempts);
+            goto finally;
+        }
+        recvbuf = PyMem_Realloc(allocated, len);
+        if (recvbuf == NULL) {
+            PyErr_NoMemory();
+            goto finally;
+        }
+        allocated = recvbuf;
+        buflen = len;
+    }
+
     if (len == 0) {
         if (errno) {
             posix_error();
-            return NULL;
         }
         else {
-            Py_RETURN_NONE;
-        }
-    }
-
-    if (len >= sizeof(buffer)) {
-        size_t len2;
-        char *buf = PyMem_Malloc(len);
-        if (buf == NULL)
-            return PyErr_NoMemory();
-        len2 = confstr(name, buf, len);
-        assert(len == len2);
-        result = PyUnicode_DecodeFSDefaultAndSize(buf, len-1);
-        PyMem_Free(buf);
-    }
-    else
-        result = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1);
+            result = Py_None;
+            Py_INCREF(Py_None);
+        }
+    }
+    else
+        result = PyUnicode_DecodeFSDefaultAndSize(recvbuf, len-1);
+finally:
+    PyMem_Free(allocated);
     return result;
 }
 #endif /* HAVE_CONFSTR */


More information about the Python-bugs-list mailing list