Attempt to make QFile I/O 64-bit safe

Use qint64 wherever possible. The linear buffer is never requested to
allocate that much memory (always limited), but at least we ensure we're
not dropping bits where we shouldn't.

Windows's POSIX compatibility layer is never largefile enabled, so it is
always necessary to chunk large reads and writes. On Unix, this will
be rare, unless someone passed -no-largefile to configure, for some
weird reason.

Unfortunately, this is not testable, unless we can allocate a buffer
with 4 GB or more in size. The test for this would be to open a file we
know to be small, then try to read 4 GB + 1 byte. If everything works
correctly, we'll read the full file; if there was a truncation, we'd
read one byte.

Change-Id: If3ee511bf1de17e0123c85bbcaa463b9972746ce
Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Thiago Macieira 2014-09-19 14:17:46 -07:00
parent f91b81ca39
commit 85da1625e4
3 changed files with 39 additions and 22 deletions

View File

@ -73,6 +73,17 @@ QT_BEGIN_NAMESPACE
# endif # endif
#endif #endif
#ifdef Q_OS_WIN
// on Windows, read() and write() use int and unsigned int
typedef int SignedIOType;
typedef unsigned int UnsignedIOType;
#else
typedef ssize_t SignedIOType;
typedef size_t UnsignedIOType;
Q_STATIC_ASSERT_X(sizeof(SignedIOType) == sizeof(UnsignedIOType),
"Unsupported: read/write return a type with different size as the len parameter");
#endif
/*! \class QFSFileEngine /*! \class QFSFileEngine
\inmodule QtCore \inmodule QtCore
\brief The QFSFileEngine class implements Qt's default file engine. \brief The QFSFileEngine class implements Qt's default file engine.
@ -605,13 +616,16 @@ qint64 QFSFileEnginePrivate::readFdFh(char *data, qint64 len)
} else if (fd != -1) { } else if (fd != -1) {
// Unbuffered stdio mode. // Unbuffered stdio mode.
#ifdef Q_OS_WIN SignedIOType result;
int result;
#else
ssize_t result;
#endif
do { do {
result = QT_READ(fd, data + readBytes, size_t(len - readBytes)); // calculate the chunk size
// on Windows or 32-bit no-largefile Unix, we'll need to read in chunks
// we limit to the size of the signed type, otherwise we could get a negative number as a result
quint64 wantedBytes = quint64(len) - quint64(readBytes);
UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max();
if (chunkSize > wantedBytes)
chunkSize = wantedBytes;
result = QT_READ(fd, data + readBytes, chunkSize);
} while (result > 0 && (readBytes += result) < len); } while (result > 0 && (readBytes += result) < len);
eof = !(result == -1); eof = !(result == -1);
@ -722,13 +736,16 @@ qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len)
} else if (fd != -1) { } else if (fd != -1) {
// Unbuffered stdio mode. // Unbuffered stdio mode.
#ifdef Q_OS_WIN SignedIOType result;
int result;
#else
ssize_t result;
#endif
do { do {
result = QT_WRITE(fd, data + writtenBytes, size_t(len - writtenBytes)); // calculate the chunk size
// on Windows or 32-bit no-largefile Unix, we'll need to read in chunks
// we limit to the size of the signed type, otherwise we could get a negative number as a result
quint64 wantedBytes = quint64(len) - quint64(writtenBytes);
UnsignedIOType chunkSize = std::numeric_limits<SignedIOType>::max();
if (chunkSize > wantedBytes)
chunkSize = wantedBytes;
result = QT_WRITE(fd, data + writtenBytes, chunkSize);
} while (result > 0 && (writtenBytes += result) < len); } while (result > 0 && (writtenBytes += result) < len);
} }

View File

@ -786,7 +786,7 @@ qint64 QIODevice::read(char *data, qint64 maxSize)
bool moreToRead = true; bool moreToRead = true;
do { do {
// Try reading from the buffer. // Try reading from the buffer.
int lastReadChunkSize = d->buffer.read(data, maxSize); qint64 lastReadChunkSize = d->buffer.read(data, maxSize);
if (lastReadChunkSize > 0) { if (lastReadChunkSize > 0) {
*d->pPos += lastReadChunkSize; *d->pPos += lastReadChunkSize;
readSoFar += lastReadChunkSize; readSoFar += lastReadChunkSize;

View File

@ -98,19 +98,19 @@ public:
first++; first++;
return ch; return ch;
} }
int read(char* target, qint64 size) { qint64 read(char* target, qint64 size) {
int r = qMin(size, len); qint64 r = qMin(size, len);
memcpy(target, first, r); memcpy(target, first, r);
len -= r; len -= r;
first += r; first += r;
return r; return r;
} }
int peek(char* target, qint64 size) { qint64 peek(char* target, qint64 size) {
int r = qMin(size, len); qint64 r = qMin(size, len);
memcpy(target, first, r); memcpy(target, first, r);
return r; return r;
} }
char* reserve(int size) { char* reserve(qint64 size) {
makeSpace(size + len, freeSpaceAtEnd); makeSpace(size + len, freeSpaceAtEnd);
char* writePtr = first + len; char* writePtr = first + len;
len += size; len += size;
@ -128,16 +128,16 @@ public:
clear(); clear();
return retVal; return retVal;
} }
int readLine(char* target, qint64 size) { qint64 readLine(char* target, qint64 size) {
int r = qMin(size, len); qint64 r = qMin(size, len);
char* eol = static_cast<char*>(memchr(first, '\n', r)); char* eol = static_cast<char*>(memchr(first, '\n', r));
if (eol) if (eol)
r = 1+(eol-first); r = 1+(eol-first);
memcpy(target, first, r); memcpy(target, first, r);
len -= r; len -= r;
first += r; first += r;
return int(r); return r;
} }
bool canReadLine() const { bool canReadLine() const {
return memchr(first, '\n', len); return memchr(first, '\n', len);
} }