From 65a358c68b4e9411ac3a8667db3f3150f93a106d Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Fri, 17 Apr 2009 13:19:17 +0000 Subject: [PATCH] 2009-04-17 Tatsuhiro Tsujikawa Fixed segmentation fault when GZipDecoder::decode() returns 0 byte. * src/DownloadCommand.cc * src/bitfield.h * test/bitfieldTest.cc Fixed the bug that causes infinite loop if broken web server returns chunked response without last "0" chunk-size marker and closes connection. * src/DownloadCommand.cc Instantiate properly configured HttpDownloadCommand for non-resumable downlaods. * src/HttpResponseCommand.cc --- ChangeLog | 17 +++++++++++++++++ src/DownloadCommand.cc | 14 ++++++++------ src/HttpResponseCommand.cc | 26 +++++++++++++++++++++++--- src/bitfield.h | 3 +++ test/bitfieldTest.cc | 1 + 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index cf206de1..768d45fa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2009-04-17 Tatsuhiro Tsujikawa + + Fixed segmentation fault when GZipDecoder::decode() returns 0 + byte. + * src/DownloadCommand.cc + * src/bitfield.h + * test/bitfieldTest.cc + + Fixed the bug that causes infinite loop if broken web server + returns chunked response without last "0" chunk-size marker and + closes connection. + * src/DownloadCommand.cc + + Instantiate properly configured HttpDownloadCommand for + non-resumable downlaods. + * src/HttpResponseCommand.cc + 2009-04-16 Tatsuhiro Tsujikawa Rewritten array operation functions. diff --git a/src/DownloadCommand.cc b/src/DownloadCommand.cc index da7c35a1..c47f0f8f 100644 --- a/src/DownloadCommand.cc +++ b/src/DownloadCommand.cc @@ -156,17 +156,14 @@ bool DownloadCommand::executeInternal() { } #endif // ENABLE_MESSAGE_DIGEST + if(bufSizeFinal > 0) { + segment->updateWrittenLength(bufSizeFinal); + } - segment->updateWrittenLength(bufSizeFinal); - peerStat->updateDownloadLength(bufSize); _requestGroup->getSegmentMan()->updateDownloadSpeedFor(peerStat); - if(_requestGroup->getTotalLength() != 0 && bufSize == 0 && - !socket->wantRead() && !socket->wantWrite()) { - throw DlRetryEx(EX_GOT_EOF); - } bool segmentComplete = false; if(_transferEncodingDecoder.isNull() && _contentEncodingDecoder.isNull()) { if(segment->complete()) { @@ -193,6 +190,11 @@ bool DownloadCommand::executeInternal() { } } + if(!segmentComplete && bufSize == 0 && + !socket->wantRead() && !socket->wantWrite()) { + throw DlRetryEx(EX_GOT_EOF); + } + if(segmentComplete) { logger->info(MSG_SEGMENT_DOWNLOAD_COMPLETED, cuid); #ifdef ENABLE_MESSAGE_DIGEST diff --git a/src/HttpResponseCommand.cc b/src/HttpResponseCommand.cc index 62723a87..d1704418 100644 --- a/src/HttpResponseCommand.cc +++ b/src/HttpResponseCommand.cc @@ -68,6 +68,12 @@ namespace aria2 { +static SharedHandle getTransferEncodingDecoder +(const SharedHandle& httpResponse); + +static SharedHandle getContentEncodingDecoder +(const SharedHandle& httpResponse); + HttpResponseCommand::HttpResponseCommand(int32_t cuid, const RequestHandle& req, RequestGroup* requestGroup, @@ -147,6 +153,8 @@ bool HttpResponseCommand::executeInternal() if(req->getMethod() == Request::METHOD_GET && (totalLength != 0 || !httpResponse->getHttpHeader()->defined(HttpHeader::CONTENT_LENGTH))){ + // dctx->knowsTotalLength() == true only when server says the + // size of file is 0 explicitly. dctx->markTotalLengthIsUnknown(); } return handleOtherEncoding(httpResponse); @@ -158,8 +166,19 @@ bool HttpResponseCommand::executeInternal() _requestGroup->validateTotalLength(httpResponse->getEntityLength()); // update last modified time updateLastModifiedTime(httpResponse->getLastModifiedTime()); - - e->commands.push_back(createHttpDownloadCommand(httpResponse)); + if(_requestGroup->getTotalLength() == 0) { + // Since total length is unknown, the file size in previously + // failed download could be larger than the size this time. + // Also we can't resume in this case too. So truncate the file + // anyway. + _requestGroup->getPieceStorage()->getDiskAdaptor()->truncate(0); + e->commands.push_back + (createHttpDownloadCommand(httpResponse, + getTransferEncodingDecoder(httpResponse), + getContentEncodingDecoder(httpResponse))); + } else { + e->commands.push_back(createHttpDownloadCommand(httpResponse)); + } return true; } } @@ -299,11 +318,12 @@ bool HttpResponseCommand::handleOtherEncoding(const HttpResponseHandle& httpResp _requestGroup->shouldCancelDownloadForSafety(); _requestGroup->getPieceStorage()->getDiskAdaptor()->initAndOpenFile(); + // In this context, knowsTotalLength() is true only when the file is + // really zero-length. if(_requestGroup->getDownloadContext()->knowsTotalLength()) { poolConnection(); return true; } - e->commands.push_back (createHttpDownloadCommand(httpResponse, getTransferEncodingDecoder(httpResponse), diff --git a/src/bitfield.h b/src/bitfield.h index 8b1b8d4f..2c000031 100644 --- a/src/bitfield.h +++ b/src/bitfield.h @@ -91,6 +91,9 @@ inline size_t countBit32(uint32_t n) // Counts set bit in bitfield. inline size_t countSetBit(const unsigned char* bitfield, size_t nbits) { + if(nbits == 0) { + return 0; + } size_t count = 0; size_t size = sizeof(uint32_t); size_t len = (nbits+7)/8; diff --git a/test/bitfieldTest.cc b/test/bitfieldTest.cc index b6b95e77..6577093e 100644 --- a/test/bitfieldTest.cc +++ b/test/bitfieldTest.cc @@ -45,6 +45,7 @@ void bitfieldTest::testCountSetBit() CPPUNIT_ASSERT_EQUAL((size_t)37, bitfield::countSetBit(bitfield, 48)); CPPUNIT_ASSERT_EQUAL((size_t)36, bitfield::countSetBit(bitfield, 47)); CPPUNIT_ASSERT_EQUAL((size_t)28, bitfield::countSetBit(bitfield, 32)); + CPPUNIT_ASSERT_EQUAL((size_t)0, bitfield::countSetBit(bitfield, 0)); } void bitfieldTest::testLastByteMask()