Author: Alex Gaynor <alex.gay...@gmail.com> Branch: merge-2.7.2 Changeset: r51667:b0b180ee94ea Date: 2012-01-22 15:26 -0600 http://bitbucket.org/pypy/pypy/changeset/b0b180ee94ea/
Log: Fix for mmap when trying to specify an offset that's past the end of a file. diff --git a/pypy/module/mmap/test/test_mmap.py b/pypy/module/mmap/test/test_mmap.py --- a/pypy/module/mmap/test/test_mmap.py +++ b/pypy/module/mmap/test/test_mmap.py @@ -8,7 +8,7 @@ space = gettestobjspace(usemodules=('mmap',)) cls.space = space cls.w_tmpname = space.wrap(str(udir.join('mmap-'))) - + def test_page_size(self): import mmap assert mmap.PAGESIZE > 0 @@ -43,7 +43,7 @@ raises(TypeError, mmap, "foo") raises(TypeError, mmap, 0, "foo") - + if os.name == "posix": raises(ValueError, mmap, 0, 1, 2, 3, 4) raises(TypeError, mmap, 0, 1, 2, 3, "foo", 5) @@ -72,7 +72,7 @@ from mmap import mmap f = open(self.tmpname + "a", "w+") - + f.write("c") f.flush() raises(ValueError, mmap, f.fileno(), 123) @@ -81,18 +81,18 @@ def test_create(self): from mmap import mmap f = open(self.tmpname + "b", "w+") - + f.write("c") f.flush() m = mmap(f.fileno(), 1) assert m.read(99) == "c" - + f.close() def test_close(self): from mmap import mmap f = open(self.tmpname + "c", "w+") - + f.write("c") f.flush() m = mmap(f.fileno(), 1) @@ -131,7 +131,7 @@ def test_read(self): from mmap import mmap f = open(self.tmpname + "f", "w+") - + f.write("foobar") f.flush() m = mmap(f.fileno(), 6) @@ -217,7 +217,7 @@ def test_is_modifiable(self): import mmap f = open(self.tmpname + "h", "w+") - + f.write("foobar") f.flush() m = mmap.mmap(f.fileno(), 6, access=mmap.ACCESS_READ) @@ -229,7 +229,7 @@ def test_seek(self): from mmap import mmap f = open(self.tmpname + "i", "w+") - + f.write("foobar") f.flush() m = mmap(f.fileno(), 6) @@ -270,7 +270,7 @@ def test_write_byte(self): import mmap f = open(self.tmpname + "k", "w+") - + f.write("foobar") f.flush() m = mmap.mmap(f.fileno(), 6, access=mmap.ACCESS_READ) @@ -286,7 +286,7 @@ def test_size(self): from mmap import mmap f = open(self.tmpname + "l", "w+") - + f.write("foobar") f.flush() m = mmap(f.fileno(), 5) @@ -297,7 +297,7 @@ def test_tell(self): from mmap import mmap f = open(self.tmpname + "m", "w+") - + f.write("c") f.flush() m = mmap(f.fileno(), 1) @@ -308,7 +308,7 @@ def test_flush(self): from mmap import mmap f = open(self.tmpname + "n", "w+") - + f.write("foobar") f.flush() m = mmap(f.fileno(), 6) @@ -319,10 +319,18 @@ m.close() f.close() + def test_length_0_large_offset(self): + import mmap + + with open(self.tmpname, "wb") as f: + f.write(115699 * 'm') + with open(self.tmpname, "w+b") as f: + raises(ValueError, mmap.mmap, f.fileno(), 0, offset=2147418112) + def test_move(self): import mmap f = open(self.tmpname + "o", "w+") - + f.write("foobar") f.flush() m = mmap.mmap(f.fileno(), 6, access=mmap.ACCESS_READ) @@ -339,15 +347,15 @@ assert a == "frarar" m.close() f.close() - + def test_resize(self): import sys if ("darwin" in sys.platform) or ("freebsd" in sys.platform): skip("resize does not work under OSX or FreeBSD") - + import mmap import os - + f = open(self.tmpname + "p", "w+") f.write("foobar") f.flush() @@ -368,10 +376,10 @@ import sys if ("darwin" not in sys.platform) and ("freebsd" not in sys.platform): skip("resize works under not OSX or FreeBSD") - + import mmap import os - + f = open(self.tmpname + "p", "w+") f.write("foobar") f.flush() @@ -388,7 +396,7 @@ def test_len(self): from mmap import mmap - + f = open(self.tmpname + "q", "w+") f.write("foobar") f.flush() @@ -397,14 +405,14 @@ assert len(m) == 6 m.close() f.close() - + def test_get_item(self): from mmap import mmap - + f = open(self.tmpname + "r", "w+") f.write("foobar") f.flush() - + m = mmap(f.fileno(), 6) fn = lambda: m["foo"] raises(TypeError, fn) @@ -416,10 +424,10 @@ assert m[4:1:-2] == 'ao' m.close() f.close() - + def test_set_item(self): import mmap - + f = open(self.tmpname + "s", "w+") f.write("foobar") f.flush() @@ -455,14 +463,14 @@ assert data == "yxxBaR" m.close() f.close() - + def test_del_item(self): from mmap import mmap - + f = open(self.tmpname + "t", "w+") f.write("foobar") f.flush() - + m = mmap(f.fileno(), 6) def fn(): del m["foo"] raises(TypeError, fn) @@ -475,11 +483,11 @@ def test_concatenation(self): from mmap import mmap - + f = open(self.tmpname + "u", "w+") f.write("foobar") f.flush() - + m = mmap(f.fileno(), 6) def fn(): m + 1 raises((SystemError, TypeError), fn) # SystemError is in CPython, @@ -492,11 +500,11 @@ def test_repeatition(self): from mmap import mmap - + f = open(self.tmpname + "v", "w+") f.write("foobar") f.flush() - + m = mmap(f.fileno(), 6) def fn(): m * 1 raises((SystemError, TypeError), fn) # SystemError is in CPython, @@ -506,14 +514,14 @@ raises((SystemError, TypeError), fn) m.close() f.close() - + def test_slicing(self): from mmap import mmap f = open(self.tmpname + "v", "w+") f.write("foobar") f.flush() - + f.seek(0) m = mmap(f.fileno(), 6) assert m[-3:7] == "bar" @@ -589,11 +597,11 @@ from mmap import PAGESIZE import sys import os - + filename = self.tmpname + "w" - + f = open(filename, "w+") - + # write 2 pages worth of data to the file f.write('\0' * PAGESIZE) f.write('foo') @@ -601,24 +609,24 @@ f.flush() m = mmap.mmap(f.fileno(), 2 * PAGESIZE) f.close() - + # sanity checks assert m.find("foo") == PAGESIZE assert len(m) == 2 * PAGESIZE assert m[0] == '\0' assert m[0:3] == '\0\0\0' - + # modify the file's content m[0] = '3' m[PAGESIZE+3:PAGESIZE+3+3] = 'bar' - + # check that the modification worked assert m[0] == '3' assert m[0:3] == '3\0\0' assert m[PAGESIZE-1:PAGESIZE+7] == '\0foobar\0' m.flush() - + # test seeking around m.seek(0,0) assert m.tell() == 0 @@ -626,28 +634,28 @@ assert m.tell() == 42 m.seek(0, 2) assert m.tell() == len(m) - + raises(ValueError, m.seek, -1) raises(ValueError, m.seek, 1, 2) raises(ValueError, m.seek, -len(m) - 1, 2) - + # try resizing map if not (("darwin" in sys.platform) or ("freebsd" in sys.platform)): m.resize(512) - + assert len(m) == 512 raises(ValueError, m.seek, 513, 0) - + # check that the underlying file is truncated too f = open(filename) f.seek(0, 2) assert f.tell() == 512 f.close() assert m.size() == 512 - + m.close() f.close() - + # test access=ACCESS_READ mapsize = 10 f = open(filename, "wb") @@ -667,21 +675,21 @@ if not (("darwin" in sys.platform) or ("freebsd" in sys.platform)): raises(TypeError, m.resize, 2 * mapsize) assert open(filename, "rb").read() == 'a' * mapsize - + # opening with size too big f = open(filename, "r+b") if not os.name == "nt": # this should work under windows raises(ValueError, mmap.mmap, f.fileno(), mapsize + 1) f.close() - + # if _MS_WINDOWS: # # repair damage from the resizing test. # f = open(filename, 'r+b') # f.truncate(mapsize) # f.close() m.close() - + # test access=ACCESS_WRITE" f = open(filename, "r+b") m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE) @@ -696,7 +704,7 @@ stuff = f.read() f.close() assert stuff == 'c' * mapsize - + # test access=ACCESS_COPY f = open(filename, "r+b") m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY) @@ -710,12 +718,12 @@ raises(TypeError, m.resize, 2 * mapsize) m.close() f.close() - + # test invalid access f = open(filename, "r+b") raises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4) f.close() - + # test incompatible parameters if os.name == "posix": f = open(filename, "r+b") @@ -723,10 +731,10 @@ prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE) f.close() - + # bad file descriptor raises(EnvironmentError, mmap.mmap, -2, 4096) - + # do a tougher .find() test. SF bug 515943 pointed out that, in 2.2, # searching for data with embedded \0 bytes didn't work. f = open(filename, 'w+') @@ -736,14 +744,14 @@ f.flush() m = mmap.mmap(f.fileno(), n) f.close() - + for start in range(n + 1): for finish in range(start, n + 1): sl = data[start:finish] assert m.find(sl) == data.find(sl) assert m.find(sl + 'x') == -1 m.close() - + # test mapping of entire file by passing 0 for map length f = open(filename, "w+") f.write(2**16 * 'm') @@ -754,7 +762,7 @@ assert m.read(2**16) == 2**16 * "m" m.close() f.close() - + # make move works everywhere (64-bit format problem earlier) f = open(filename, 'w+') f.write("ABCDEabcde") diff --git a/pypy/rlib/rmmap.py b/pypy/rlib/rmmap.py --- a/pypy/rlib/rmmap.py +++ b/pypy/rlib/rmmap.py @@ -43,7 +43,7 @@ constants = {} if _POSIX: # constants, look in sys/mman.h and platform docs for the meaning - # some constants are linux only so they will be correctly exposed outside + # some constants are linux only so they will be correctly exposed outside # depending on the OS constant_names = ['MAP_SHARED', 'MAP_PRIVATE', 'PROT_READ', 'PROT_WRITE', @@ -136,7 +136,7 @@ ) SYSINFO_UNION = rffi.CStruct( - 'union SYSINFO_UNION', + 'union SYSINFO_UNION', ("dwOemId", DWORD), ("_struct_", SYSINFO_STRUCT), ) @@ -250,7 +250,7 @@ elif _POSIX: self.fd = -1 self.closed = False - + def check_valid(self): if _MS_WINDOWS: to_close = self.map_handle == INVALID_HANDLE @@ -259,11 +259,11 @@ if to_close: raise RValueError("map closed or invalid") - + def check_writeable(self): if not (self.access != ACCESS_READ): raise RTypeError("mmap can't modify a readonly memory map.") - + def check_resizeable(self): if not (self.access == ACCESS_WRITE or self.access == _ACCESS_DEFAULT): raise RTypeError("mmap can't resize a readonly or copy-on-write memory map.") @@ -273,7 +273,7 @@ assert size >= 0 self.data = data self.size = size - + def close(self): if _MS_WINDOWS: if self.size > 0: @@ -302,7 +302,7 @@ def unmapview(self): UnmapViewOfFile(self.getptr(0)) - + def read_byte(self): self.check_valid() @@ -312,7 +312,7 @@ return value else: raise RValueError("read byte out of range") - + def readline(self): self.check_valid() @@ -327,7 +327,7 @@ res = "".join([data[i] for i in range(self.pos, eol)]) self.pos += len(res) return res - + def read(self, num=-1): self.check_valid() @@ -389,10 +389,10 @@ def seek(self, pos, whence=0): self.check_valid() - + dist = pos how = whence - + if how == 0: # relative to start where = dist elif how == 1: # relative to current position @@ -404,16 +404,16 @@ if not (0 <= where <= self.size): raise RValueError("seek out of range") - + self.pos = where - + def tell(self): self.check_valid() return self.pos - + def file_size(self): self.check_valid() - + size = self.size if _MS_WINDOWS: if self.file_handle != INVALID_HANDLE: @@ -433,11 +433,11 @@ else: size = int(size) return size - + def write(self, data): - self.check_valid() + self.check_valid() self.check_writeable() - + data_len = len(data) if self.pos + data_len > self.size: raise RValueError("data out of range") @@ -447,10 +447,10 @@ for i in range(data_len): internaldata[start+i] = data[i] self.pos = start + data_len - + def write_byte(self, byte): self.check_valid() - + if len(byte) != 1: raise RTypeError("write_byte() argument must be char") @@ -491,14 +491,14 @@ if res == -1: errno = rposix.get_errno() raise OSError(errno, os.strerror(errno)) - + return 0 - + def move(self, dest, src, count): self.check_valid() - + self.check_writeable() - + # check boundings if (src < 0 or dest < 0 or count < 0 or src + count > self.size or dest + count > self.size): @@ -507,19 +507,19 @@ datasrc = self.getptr(src) datadest = self.getptr(dest) c_memmove(datadest, datasrc, count) - + def resize(self, newsize): self.check_valid() - + self.check_resizeable() - + if _POSIX: if not has_mremap: raise RValueError("mmap: resizing not available--no mremap()") - + # resize the underlying file first os.ftruncate(self.fd, self.offset + newsize) - + # now resize the mmap newdata = c_mremap(self.getptr(0), self.size, newsize, MREMAP_MAYMOVE or 0) @@ -573,9 +573,9 @@ def len(self): self.check_valid() - + return self.size - + def getitem(self, index): self.check_valid() # simplified version, for rpython @@ -650,6 +650,8 @@ size = int(size) if stat.S_ISREG(mode): if map_size == 0: + if offset > st[stat.ST_SIZE]: + raise RValueError("mmap offset is greater than file size") map_size = size elif map_size > size: raise RValueError("mmap length is greater than file size") @@ -673,7 +675,7 @@ if res == rffi.cast(PTR, -1): errno = rposix.get_errno() raise OSError(errno, os.strerror(errno)) - + m.setdata(res, map_size) return m @@ -704,7 +706,7 @@ alloc._annenforceargs_ = (int,) free = c_munmap_safe - + elif _MS_WINDOWS: def mmap(fileno, length, tagname="", access=_ACCESS_DEFAULT, offset=0): # check size boundaries @@ -712,11 +714,11 @@ map_size = length if offset < 0: raise RValueError("negative offset") - + flProtect = 0 dwDesiredAccess = 0 fh = NULL_HANDLE - + if access == ACCESS_READ: flProtect = PAGE_READONLY dwDesiredAccess = FILE_MAP_READ @@ -728,7 +730,7 @@ dwDesiredAccess = FILE_MAP_COPY else: raise RValueError("mmap invalid access parameter.") - + # assume -1 and 0 both mean invalid file descriptor # to 'anonymously' map memory. if fileno != -1 and fileno != 0: @@ -739,7 +741,7 @@ # Win9x appears to need us seeked to zero # SEEK_SET = 0 # libc._lseek(fileno, 0, SEEK_SET) - + m = MMap(access, offset) m.file_handle = INVALID_HANDLE m.map_handle = INVALID_HANDLE @@ -752,7 +754,7 @@ res = DuplicateHandle(GetCurrentProcess(), # source process handle fh, # handle to be duplicated GetCurrentProcess(), # target process handle - handle_ref, # result + handle_ref, # result 0, # access - ignored due to options value False, # inherited by child procs? DUPLICATE_SAME_ACCESS) # options @@ -761,7 +763,7 @@ m.file_handle = handle_ref[0] finally: lltype.free(handle_ref, flavor='raw') - + if not map_size: low, high = _get_file_size(fh) if _64BIT: @@ -775,7 +777,7 @@ if tagname: m.tagname = tagname - + # DWORD is a 4-byte int. If int > 4-byte it must be divided if _64BIT: size_hi = (map_size + offset) >> 32 @@ -807,7 +809,7 @@ def alloc(map_size): """Allocate memory. This is intended to be used by the JIT, - so the memory has the executable bit set. + so the memory has the executable bit set. XXX implement me: it should get allocated internally in case of a sandboxed process """ @@ -825,5 +827,5 @@ def free(ptr, map_size): VirtualFree(ptr, 0, MEM_RELEASE) - + # register_external here? _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit