Anonymous avatar Anonymous committed 7c93368

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.

Comments (0)

Files changed (6)

Include/fileobject.h

 	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;

Lib/test/test_file2k.py

         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())
     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):

Lib/test/test_sys.py

         # 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
 Damon Kohler
 Joseph Koshy
 Maksim Kozyarchuk
+Stefan Krah
 Bob Kras
 Holger Krekel
 Michael Kremer
 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.
 

Objects/fileobject.c

 	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;
 	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. */
 
 	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;
 
 
 	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 &&
 
 	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 &&
 		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 &&
 
 	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 &&
 
 	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 &&
 	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;
 	assert(seq != NULL);
 	if (f->f_fp == NULL)
 		return err_closed();
+	if (!f->writable)
+		return err_mode("writing");
 
 	result = NULL;
 	list = NULL;
 
 	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) {
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.