]> granicus.if.org Git - python/commitdiff
Merged revisions 77989 via svnmerge from
authorAntoine Pitrou <solipsis@pitrou.net>
Fri, 5 Feb 2010 17:11:32 +0000 (17:11 +0000)
committerAntoine Pitrou <solipsis@pitrou.net>
Fri, 5 Feb 2010 17:11:32 +0000 (17:11 +0000)
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.
........

Include/fileobject.h
Lib/test/test_file.py
Lib/test/test_sys.py
Misc/ACKS
Misc/NEWS
Objects/fileobject.c

index 56cf40a364725107f80f5745e9f128bba6db73c9..96c1e5739f6d2a75c93382d8c631fe0f87f8fac3 100644 (file)
@@ -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;
index b4f494ba96a234fe4d6806db56f66a0437d5b580..ed100c14b750af81a4ba87be101ff5dfa7023931 100644 (file)
@@ -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):
index a8829849b86adb69d78bd34a61938dc5e568b62e..149f473d9ea20e9ca70d5967802126d9c6feea42 100644 (file)
@@ -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
index 528281d3a5c9a4faf31261a2eafd6d6f85b03f27..6562b7e75a3ee0d1a9ae0a8b9a91bdbb1bff9ab6 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -391,6 +391,7 @@ Pat Knight
 Greg Kochanski
 Damon Kohler
 Joseph Koshy
+Stefan Krah
 Bob Kras
 Holger Krekel
 Michael Kremer
index 3c19cce6ab7e9f7bc01d16748ca5245887e89e1a..d6c81b31651c9b0a0079fc7469aaf1377385835e 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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
index e01f38efdd0dc87247ac2a9d25a5852759892d43..52d81206e847f0b0b1fe44964ef058600fc6c7c9 100644 (file)
@@ -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) {