summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/fileobject.h2
-rw-r--r--Lib/test/test_file2k.py36
-rw-r--r--Lib/test/test_sys.py2
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS5
-rw-r--r--Objects/fileobject.c32
6 files changed, 77 insertions, 1 deletions
diff --git a/Include/fileobject.h b/Include/fileobject.h
index 77903be..83e9c25 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_file2k.py b/Lib/test/test_file2k.py
index bf47c6f..5c2de37 100644
--- a/Lib/test/test_file2k.py
+++ b/Lib/test/test_file2k.py
@@ -86,6 +86,8 @@ class AutoFileTests(unittest.TestCase):
self.assertTrue(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.assertTrue(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 6b45d12..f7e828b 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -581,7 +581,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
diff --git a/Misc/ACKS b/Misc/ACKS
index b5c3dc6..089e463 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -417,6 +417,7 @@ Greg Kochanski
Damon Kohler
Joseph Koshy
Maksim Kozyarchuk
+Stefan Krah
Bob Kras
Holger Krekel
Michael Kremer
diff --git a/Misc/NEWS b/Misc/NEWS
index 9225c6f..66748ef 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,11 @@ What's New in Python 2.7 alpha 3?
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 #7853: Normalize exceptions before they are passed to a context managers
__exit__ method.
diff --git a/Objects/fileobject.c b/Objects/fileobject.c
index 3220702..6456368 100644
--- a/Objects/fileobject.c
+++ b/Objects/fileobject.c
@@ -167,6 +167,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;
@@ -568,6 +575,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. */
@@ -782,6 +796,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;
@@ -1030,6 +1046,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 &&
@@ -1099,6 +1117,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 &&
@@ -1470,6 +1490,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 &&
@@ -1558,6 +1580,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 &&
@@ -1591,6 +1615,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 &&
@@ -1709,6 +1735,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;
@@ -1746,6 +1774,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;
@@ -2186,6 +2216,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) {