diff options
author | Antoine Pitrou <solipsis@pitrou.net> | 2010-02-05 17:11:32 (GMT) |
---|---|---|
committer | Antoine Pitrou <solipsis@pitrou.net> | 2010-02-05 17:11:32 (GMT) |
commit | 2483728850cc3c12920d7794ac01a3e890995622 (patch) | |
tree | 8ff5e130979103dda76979f4fa5e962ddf266a08 | |
parent | 6e127db0ed5288f1917bb166b561bd601fcc51c6 (diff) | |
download | cpython-2483728850cc3c12920d7794ac01a3e890995622.zip cpython-2483728850cc3c12920d7794ac01a3e890995622.tar.gz cpython-2483728850cc3c12920d7794ac01a3e890995622.tar.bz2 |
Merged revisions 77989 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r77989 | antoine.pitrou | 2010-02-05 18:05:54 +0100 (ven., 05 févr. 2010) | 6 lines
Issue #5677: Explicitly forbid write operations on read-only file objects,
and read operations on write-only file objects. On Windows, the system C
library would return a bogus result; on Solaris, it was possible to crash
the interpreter. Patch by Stefan Krah.
........
-rw-r--r-- | Include/fileobject.h | 2 | ||||
-rw-r--r-- | Lib/test/test_file.py | 36 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 2 | ||||
-rw-r--r-- | Misc/ACKS | 1 | ||||
-rw-r--r-- | Misc/NEWS | 5 | ||||
-rw-r--r-- | Objects/fileobject.c | 32 |
6 files changed, 77 insertions, 1 deletions
diff --git a/Include/fileobject.h b/Include/fileobject.h index 56cf40a..96c1e57 100644 --- a/Include/fileobject.h +++ b/Include/fileobject.h @@ -28,6 +28,8 @@ typedef struct { PyObject *weakreflist; /* List of weak references */ int unlocked_count; /* Num. currently running sections of code using f_fp with the GIL released. */ + int readable; + int writable; } PyFileObject; PyAPI_DATA(PyTypeObject) PyFile_Type; diff --git a/Lib/test/test_file.py b/Lib/test/test_file.py index b4f494b..ed100c1 100644 --- a/Lib/test/test_file.py +++ b/Lib/test/test_file.py @@ -86,6 +86,8 @@ class AutoFileTests(unittest.TestCase): self.assert_(repr(self.f).startswith("<open file '" + TESTFN)) def testErrors(self): + self.f.close() + self.f = open(TESTFN, 'rb') f = self.f self.assertEquals(f.name, TESTFN) self.assert_(not f.isatty()) @@ -123,6 +125,40 @@ class AutoFileTests(unittest.TestCase): def testReadWhenWriting(self): self.assertRaises(IOError, self.f.read) + def testIssue5677(self): + # Remark: Do not perform more than one test per open file, + # since that does NOT catch the readline error on Windows. + data = 'xxx' + for mode in ['w', 'wb', 'a', 'ab']: + for attr in ['read', 'readline', 'readlines']: + self.f = open(TESTFN, mode) + self.f.write(data) + self.assertRaises(IOError, getattr(self.f, attr)) + self.f.close() + + self.f = open(TESTFN, mode) + self.f.write(data) + self.assertRaises(IOError, lambda: [line for line in self.f]) + self.f.close() + + self.f = open(TESTFN, mode) + self.f.write(data) + self.assertRaises(IOError, self.f.readinto, bytearray(len(data))) + self.f.close() + + for mode in ['r', 'rb', 'U', 'Ub', 'Ur', 'rU', 'rbU', 'rUb']: + self.f = open(TESTFN, mode) + self.assertRaises(IOError, self.f.write, data) + self.f.close() + + self.f = open(TESTFN, mode) + self.assertRaises(IOError, self.f.writelines, [data, data]) + self.f.close() + + self.f = open(TESTFN, mode) + self.assertRaises(IOError, self.f.truncate) + self.f.close() + class OtherFileTests(unittest.TestCase): def testOpenDir(self): diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index a882984..149f473 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -538,7 +538,7 @@ class SizeofTest(unittest.TestCase): # enumerate check(enumerate([]), size(h + 'l3P')) # file - check(self.file, size(h + '4P2i4P3i3Pi')) + check(self.file, size(h + '4P2i4P3i3P3i')) # float check(float(0), size(h + 'd')) # sys.floatinfo @@ -391,6 +391,7 @@ Pat Knight Greg Kochanski Damon Kohler Joseph Koshy +Stefan Krah Bob Kras Holger Krekel Michael Kremer @@ -12,6 +12,11 @@ What's New in Python 2.6.5 Core and Builtins ----------------- +- Issue #5677: Explicitly forbid write operations on read-only file objects, + and read operations on write-only file objects. On Windows, the system C + library would return a bogus result; on Solaris, it was possible to crash + the interpreter. Patch by Stefan Krah. + - Issue #7819: Check sys.call_tracing() arguments types. - Issue #7788: Fix an interpreter crash produced by deleting a list diff --git a/Objects/fileobject.c b/Objects/fileobject.c index e01f38e..52d8120 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -173,6 +173,13 @@ fill_file_fields(PyFileObject *f, FILE *fp, PyObject *name, char *mode, f->f_encoding = Py_None; Py_INCREF(Py_None); f->f_errors = Py_None; + f->readable = f->writable = 0; + if (strchr(mode, 'r') != NULL || f->f_univ_newline) + f->readable = 1; + if (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL) + f->writable = 1; + if (strchr(mode, '+') != NULL) + f->readable = f->writable = 1; if (f->f_mode == NULL) return NULL; @@ -487,6 +494,13 @@ err_closed(void) return NULL; } +static PyObject * +err_mode(char *action) +{ + PyErr_Format(PyExc_IOError, "File not open for %s", action); + return NULL; +} + /* Refuse regular file I/O if there's data in the iteration-buffer. * Mixing them would cause data to arrive out of order, as the read* * methods don't use the iteration buffer. */ @@ -701,6 +715,8 @@ file_truncate(PyFileObject *f, PyObject *args) if (f->f_fp == NULL) return err_closed(); + if (!f->writable) + return err_mode("writing"); if (!PyArg_UnpackTuple(args, "truncate", 0, 1, &newsizeobj)) return NULL; @@ -949,6 +965,8 @@ file_read(PyFileObject *f, PyObject *args) if (f->f_fp == NULL) return err_closed(); + if (!f->readable) + return err_mode("reading"); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && @@ -1018,6 +1036,8 @@ file_readinto(PyFileObject *f, PyObject *args) if (f->f_fp == NULL) return err_closed(); + if (!f->readable) + return err_mode("reading"); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && @@ -1389,6 +1409,8 @@ PyFile_GetLine(PyObject *f, int n) PyFileObject *fo = (PyFileObject *)f; if (fo->f_fp == NULL) return err_closed(); + if (!fo->readable) + return err_mode("reading"); /* refuse to mix with f.next() */ if (fo->f_buf != NULL && (fo->f_bufend - fo->f_bufptr) > 0 && @@ -1477,6 +1499,8 @@ file_readline(PyFileObject *f, PyObject *args) if (f->f_fp == NULL) return err_closed(); + if (!f->readable) + return err_mode("reading"); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && @@ -1510,6 +1534,8 @@ file_readlines(PyFileObject *f, PyObject *args) if (f->f_fp == NULL) return err_closed(); + if (!f->readable) + return err_mode("reading"); /* refuse to mix with f.next() */ if (f->f_buf != NULL && (f->f_bufend - f->f_bufptr) > 0 && @@ -1628,6 +1654,8 @@ file_write(PyFileObject *f, PyObject *args) Py_ssize_t n, n2; if (f->f_fp == NULL) return err_closed(); + if (!f->writable) + return err_mode("writing"); if (f->f_binary) { if (!PyArg_ParseTuple(args, "s*", &pbuf)) return NULL; @@ -1665,6 +1693,8 @@ file_writelines(PyFileObject *f, PyObject *seq) assert(seq != NULL); if (f->f_fp == NULL) return err_closed(); + if (!f->writable) + return err_mode("writing"); result = NULL; list = NULL; @@ -2105,6 +2135,8 @@ file_iternext(PyFileObject *f) if (f->f_fp == NULL) return err_closed(); + if (!f->readable) + return err_mode("reading"); l = readahead_get_line_skip(f, 0, READAHEAD_BUFSIZE); if (l == NULL || PyString_GET_SIZE(l) == 0) { |