summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKristján Valur Jónsson <kristjan@ccpgames.com>2009-02-04 10:05:25 (GMT)
committerKristján Valur Jónsson <kristjan@ccpgames.com>2009-02-04 10:05:25 (GMT)
commitfd4c872726e650f1e43c4eed4fa7e7e326b3fee6 (patch)
tree511e8c21bc40e5d71c8fd2328ff7fdbca2db8c11
parent8bdd3b3dcf38ba1b5bd460b3c309fb6a8ea1127c (diff)
downloadcpython-fd4c872726e650f1e43c4eed4fa7e7e326b3fee6.zip
cpython-fd4c872726e650f1e43c4eed4fa7e7e326b3fee6.tar.gz
cpython-fd4c872726e650f1e43c4eed4fa7e7e326b3fee6.tar.bz2
issue 4804: Provide checks for the format string of strftime, and for the "mode" string of fopen on Windows. These strings are user provided from python and so we can avoid invoking the C runtime invalid parameter handler by first checking that they are valid.
-rw-r--r--Lib/test/test_file.py2
-rw-r--r--Modules/timemodule.c17
-rw-r--r--Objects/fileobject.c89
3 files changed, 106 insertions, 2 deletions
diff --git a/Lib/test/test_file.py b/Lib/test/test_file.py
index b4f494b..a134a89 100644
--- a/Lib/test/test_file.py
+++ b/Lib/test/test_file.py
@@ -154,7 +154,7 @@ class OtherFileTests(unittest.TestCase):
for name in (TESTFN, unicode(TESTFN), unicode(TESTFN + '\t')):
try:
f = open(name, "rr")
- except IOError:
+ except (IOError, ValueError):
pass
else:
f.close()
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index e8de2c5..2f4092d 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -470,6 +470,23 @@ time_strftime(PyObject *self, PyObject *args)
return NULL;
}
+#ifdef MS_WINDOWS
+ /* check that the format string contains only valid directives */
+ for(outbuf = strchr(fmt, '%');
+ outbuf != NULL;
+ outbuf = strchr(outbuf+2, '%'))
+ {
+ if (outbuf[1]=='#')
+ ++outbuf; /* not documented by python, */
+ if (outbuf[1]=='\0' ||
+ !strchr("aAbBcdfHIjmMpSUwWxXyYzZ%", outbuf[1]))
+ {
+ PyErr_SetString(PyExc_ValueError, "Invalid format string");
+ return 0;
+ }
+ }
+#endif
+
fmtlen = strlen(fmt);
/* I hate these functions that presume you know how big the output
diff --git a/Objects/fileobject.c b/Objects/fileobject.c
index e01f38e..77724d4 100644
--- a/Objects/fileobject.c
+++ b/Objects/fileobject.c
@@ -181,6 +181,87 @@ fill_file_fields(PyFileObject *f, FILE *fp, PyObject *name, char *mode,
return (PyObject *) f;
}
+#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__)
+#define Py_VERIFY_WINNT
+/* The CRT on windows compiled with Visual Studio 2005 and higher may
+ * assert if given invalid mode strings. This is all fine and well
+ * in static languages like C where the mode string is typcially hard
+ * coded. But in Python, were we pass in the mode string from the user,
+ * we need to verify it first manually
+ */
+static int _PyVerify_Mode_WINNT(const char *mode)
+{
+ /* See if mode string is valid on Windows to avoid hard assertions */
+ /* remove leading spacese */
+ int singles = 0;
+ int pairs = 0;
+ int encoding = 0;
+ const char *s, *c;
+
+ while(*mode == ' ') /* strip initial spaces */
+ ++mode;
+ if (!strchr("rwa", *mode)) /* must start with one of these */
+ return 0;
+ while (*++mode) {
+ if (*mode == ' ' || *mode == 'N') /* ignore spaces and N */
+ continue;
+ s = "+TD"; /* each of this can appear only once */
+ c = strchr(s, *mode);
+ if (c) {
+ ptrdiff_t idx = s-c;
+ if (singles & (1<<idx))
+ return 0;
+ singles |= (1<<idx);
+ continue;
+ }
+ s = "btcnSR"; /* only one of each letter in the pairs allowed */
+ c = strchr(s, *mode);
+ if (c) {
+ ptrdiff_t idx = (s-c)/2;
+ if (pairs & (1<<idx))
+ return 0;
+ pairs |= (1<<idx);
+ continue;
+ }
+ if (*mode == ',') {
+ encoding = 1;
+ break;
+ }
+ return 0; /* found an invalid char */
+ }
+
+ if (encoding) {
+ char *e[] = {"UTF-8", "UTF-16LE", "UNICODE"};
+ while (*mode == ' ')
+ ++mode;
+ /* find 'ccs =' */
+ if (strncmp(mode, "ccs", 3))
+ return 0;
+ mode += 3;
+ while (*mode == ' ')
+ ++mode;
+ if (*mode != '=')
+ return 0;
+ while (*mode == ' ')
+ ++mode;
+ for(encoding = 0; encoding<_countof(e); ++encoding) {
+ size_t l = strlen(e[encoding]);
+ if (!strncmp(mode, e[encoding], l)) {
+ mode += l; /* found a valid encoding */
+ break;
+ }
+ }
+ if (encoding == _countof(e))
+ return 0;
+ }
+ /* skip trailing spaces */
+ while (*mode == ' ')
+ ++mode;
+
+ return *mode == '\0'; /* must be at the end of the string */
+}
+#endif
+
/* check for known incorrect mode strings - problem is, platforms are
free to accept any mode characters they like and are supposed to
ignore stuff they don't understand... write or append mode with
@@ -223,7 +304,13 @@ _PyFile_SanitizeMode(char *mode)
"one of 'r', 'w', 'a' or 'U', not '%.200s'", mode);
return -1;
}
-
+#ifdef Py_VERIFY_WINNT
+ /* additional checks on NT with visual studio 2005 and higher */
+ if (!_PyVerify_Mode_WINNT(mode)) {
+ PyErr_Format(PyExc_ValueError, "Invalid mode ('%.50s')", mode);
+ return -1;
+ }
+#endif
return 0;
}