tst_largefile: fix the mapOffsetOverflow case to match actual behavior

Unix mmap(2) system calls do allow for mapping beyond the end of the
file, though what happens after you try to dereference the pointers it
gives is unspecified. POSIX[1] says that implementations shouldn't allow
it:
 The system shall always zero-fill any partial page at the end of an
 object. Further, the system shall never write out any modified portions
 of the last page of an object which are beyond its end. References
 within the address range starting at pa and continuing for len bytes to
 whole pages following the end of an object shall result in delivery of
 a SIGBUS signal.

However, Linux allows this in read-write mode and extends the file
(depending on the filesystem).

Windows MapViewOfFile never allows mapping beyond the end.

[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html

Change-Id: Ie67d35dff21147e99ad9fffd14acc8d9a1a0c38d
Reviewed-by: Sami Nurmenniemi <sami.nurmenniemi@qt.io>
Reviewed-by: Teemu Holappa <teemu.holappa@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Thiago Macieira 2017-03-17 14:33:33 -07:00
parent 9b5b44207e
commit 9a0d47bcf1

View File

@ -502,23 +502,42 @@ void tst_LargeFile::mapFile()
}
//Mac: memory-mapping beyond EOF may succeed but it could generate bus error on access
//FreeBSD: same
//Linux: memory-mapping beyond EOF usually succeeds, but depends on the filesystem
// 32-bit: limited to 44-bit offsets
//Windows: memory-mapping beyond EOF is not allowed
void tst_LargeFile::mapOffsetOverflow()
{
#ifndef Q_OS_MAC
// Out-of-range mappings should fail, and not silently clip the offset
for (int i = 50; i < 63; ++i) {
enum {
#ifdef Q_OS_WIN
Succeeds = false,
MaxOffset = 63
#else
Succeeds = true,
# if (defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)) && Q_PROCESSOR_WORDSIZE == 4
MaxOffset = 43
# else
MaxOffset = 63
# endif
#endif
};
QByteArray zeroPage(blockSize, '\0');
for (int i = maxSizeBits + 1; i < 63; ++i) {
bool succeeds = Succeeds && (i <= MaxOffset);
uchar *address = 0;
qint64 offset = Q_INT64_C(1) << i;
address = largeFile.map(((qint64)1 << i), blockSize);
#if defined(__x86_64__)
QEXPECT_FAIL("", "fails on 64-bit Linux (QTBUG-21175)", Abort);
#endif
QVERIFY( !address );
if (succeeds)
QTest::ignoreMessage(QtWarningMsg, "QFSFileEngine::map: Mapping a file beyond its size is not portable");
address = largeFile.map(offset, blockSize);
QCOMPARE(!!address, succeeds);
address = largeFile.map(((qint64)1 << i) + blockSize, blockSize);
QVERIFY( !address );
if (succeeds)
QTest::ignoreMessage(QtWarningMsg, "QFSFileEngine::map: Mapping a file beyond its size is not portable");
address = largeFile.map(offset + blockSize, blockSize);
QCOMPARE(!!address, succeeds);
}
#endif
}
QTEST_APPLESS_MAIN(tst_LargeFile)