2008-01-04 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

Fixed segmentation fault when bad torrent metainfo is parsed.
	Added dynamic_cast properly to detect the misconfiguration of 
metainfo
	and then throw exception or skip it.
	* src/DefaultBtContext.{h, cc}
	* test/DefaultBtContextTest.cc
	* src/AnnounceList.cc
	* src/CompactPeerListProcessor.cc
	* src/message.h
	* src/DefaultBtAnnounce.{h, cc}
	* test/DefaultBtAnnounceTest.cc
	* src/BencodeVisitor.cc
pull/1/head
Tatsuhiro Tsujikawa 2008-01-04 12:31:49 +00:00
parent 853a8ef6aa
commit da4d9092b2
12 changed files with 229 additions and 66 deletions

View File

@ -1,3 +1,17 @@
2008-01-04 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
Fixed segmentation fault when bad torrent metainfo is parsed.
Added dynamic_cast properly to detect the misconfiguration of metainfo
and then throw exception or skip it.
* src/DefaultBtContext.{h, cc}
* test/DefaultBtContextTest.cc
* src/AnnounceList.cc
* src/CompactPeerListProcessor.cc
* src/message.h
* src/DefaultBtAnnounce.{h, cc}
* test/DefaultBtAnnounceTest.cc
* src/BencodeVisitor.cc
2007-12-29 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>
Added missing "B"(=Byte). So now the message looks like this:

View File

@ -51,12 +51,17 @@ void AnnounceList::reconfigure(const MetaEntry* announceListEntry) {
if(l) {
for(MetaList::const_iterator itr = l->getList().begin();
itr != l->getList().end(); itr++) {
const List* elem = (List*)*itr;
const List* elem = dynamic_cast<const List*>(*itr);
if(!elem) {
continue;
}
Strings urls;
for(MetaList::const_iterator elemItr = elem->getList().begin();
elemItr != elem->getList().end(); elemItr++) {
const Data* data = (Data*)*elemItr;
urls.push_back(data->toString());
const Data* data = dynamic_cast<const Data*>(*elemItr);
if(data) {
urls.push_back(data->toString());
}
}
if(urls.size()) {
AnnounceTierHandle tier(new AnnounceTier(urls));

View File

@ -75,10 +75,10 @@ void BencodeVisitor::visit(const Dictionary* d)
void BencodeVisitor::visit(const MetaEntry* e)
{
if(dynamic_cast<const Data*>(e) != 0) {
visit((const Data*)e);
visit(reinterpret_cast<const Data*>(e));
} else if(dynamic_cast<const List*>(e) != 0) {
visit((const List*)e);
visit(reinterpret_cast<const List*>(e));
} else if(dynamic_cast<const Dictionary*>(e) != 0) {
visit((const Dictionary*)e);
visit(reinterpret_cast<const Dictionary*>(e));
}
}

View File

@ -41,8 +41,10 @@ bool CompactPeerListProcessor::canHandle(const MetaEntry* peersEntry) const {
Peers CompactPeerListProcessor::extractPeer(const MetaEntry* peersEntry) {
Peers peers;
const Data* peersData = (const Data*)peersEntry;
const Data* peersData = dynamic_cast<const Data*>(peersEntry);
if(!peersData) {
return peers;
}
if(peersData->getLen()%6 == 0) {
for(int32_t i = 0; i < peersData->getLen(); i += 6) {
struct in_addr in;

View File

@ -184,41 +184,50 @@ DefaultBtAnnounce::processAnnounceResponse(const char* trackerResponse,
{
SharedHandle<MetaEntry> entry(MetaFileUtil::bdecoding(trackerResponse,
trackerResponseLength));
Dictionary* response = (Dictionary*)entry.get();
Data* failureReasonData = (Data*)response->get("failure reason");
const Dictionary* response = dynamic_cast<const Dictionary*>(entry.get());
if(!response) {
throw new DlAbortEx(MSG_NULL_TRACKER_RESPONSE);
}
const Data* failureReasonData = dynamic_cast<const Data*>(response->get("failure reason"));
if(failureReasonData) {
throw new DlAbortEx(EX_TRACKER_FAILURE,
failureReasonData->toString().c_str());
}
Data* warningMessageData = (Data*)response->get("warning message");
const Data* warningMessageData = dynamic_cast<const Data*>(response->get("warning message"));
if(warningMessageData) {
logger->warn(MSG_TRACKER_WARNING_MESSAGE,
warningMessageData->toString().c_str());
}
Data* trackerIdData = (Data*)response->get("tracker id");
const Data* trackerIdData = dynamic_cast<const Data*>(response->get("tracker id"));
if(trackerIdData) {
trackerId = trackerIdData->toString();
logger->debug("Tracker ID:%s", trackerId.c_str());
}
Data* intervalData = (Data*)response->get("interval");
const Data* intervalData = dynamic_cast<const Data*>(response->get("interval"));
if(intervalData) {
interval = intervalData->toInt();
logger->debug("Interval:%d", interval);
int32_t t = intervalData->toInt();
if(t > 0) {
interval = intervalData->toInt();
logger->debug("Interval:%d", interval);
}
}
Data* minIntervalData = (Data*)response->get("min interval");
const Data* minIntervalData = dynamic_cast<const Data*>(response->get("min interval"));
if(minIntervalData) {
minInterval = minIntervalData->toInt();
logger->debug("Min interval:%d", minInterval);
int32_t t = minIntervalData->toInt();
if(t > 0) {
minInterval = minIntervalData->toInt();
logger->debug("Min interval:%d", minInterval);
}
}
if(minInterval > interval) {
minInterval = interval;
}
Data* completeData = (Data*)response->get("complete");
const Data* completeData = dynamic_cast<const Data*>(response->get("complete"));
if(completeData) {
complete = completeData->toInt();
logger->debug("Complete:%d", complete);
}
Data* incompleteData = (Data*)response->get("incomplete");
const Data* incompleteData = dynamic_cast<const Data*>(response->get("incomplete"));
if(incompleteData) {
incomplete = incompleteData->toInt();
logger->debug("Incomplete:%d", incomplete);

View File

@ -117,6 +117,31 @@ public:
void generateKey();
void setRandomizer(const RandomizerHandle& randomizer);
int32_t getInterval() const
{
return interval;
}
int32_t getMinInterval() const
{
return minInterval;
}
int32_t getComplete() const
{
return complete;
}
int32_t getIncomplete() const
{
return incomplete;
}
const string& getTrackerID() const
{
return trackerId;
}
};
#endif // _D_DEFAULT_BT_ANNOUNCE_H_

View File

@ -44,11 +44,14 @@
#include "a2netcompat.h"
#include "AnnounceTier.h"
#include "SimpleRandomizer.h"
#include "LogFactory.h"
#include "message.h"
#include <libgen.h>
DefaultBtContext::DefaultBtContext():_peerIdPrefix("-aria2-"),
_randomizer(SimpleRandomizer::getInstance()),
_ownerRequestGroup(0) {}
_ownerRequestGroup(0),
_logger(LogFactory::getInstance()) {}
DefaultBtContext::~DefaultBtContext() {}
@ -101,11 +104,10 @@ void DefaultBtContext::extractPieceHash(const unsigned char* hashData,
}
}
void DefaultBtContext::extractFileEntries(Dictionary* infoDic,
void DefaultBtContext::extractFileEntries(const Dictionary* infoDic,
const string& defaultName,
const Strings& urlList) {
// TODO use dynamic_cast
Data* nameData = (Data*)infoDic->get("name");
const Data* nameData = dynamic_cast<const Data*>(infoDic->get("name"));
if(nameData) {
name = nameData->toString();
} else {
@ -113,8 +115,7 @@ void DefaultBtContext::extractFileEntries(Dictionary* infoDic,
name = string(basename(basec))+".file";
free(basec);
}
// TODO use dynamic_cast
List* files = (List*)infoDic->get("files");
const List* files = dynamic_cast<const List*>(infoDic->get("files"));
if(files) {
int64_t length = 0;
int64_t offset = 0;
@ -123,21 +124,36 @@ void DefaultBtContext::extractFileEntries(Dictionary* infoDic,
const MetaList& metaList = files->getList();
for(MetaList::const_iterator itr = metaList.begin();
itr != metaList.end(); itr++) {
Dictionary* fileDic = (Dictionary*)(*itr);
// TODO use dynamic_cast
Data* lengthData = (Data*)fileDic->get("length");
length += lengthData->toLLInt();
// TODO use dynamic_cast
List* pathList = (List*)fileDic->get("path");
const Dictionary* fileDic = dynamic_cast<const Dictionary*>((*itr));
if(!fileDic) {
continue;
}
const Data* lengthData = dynamic_cast<const Data*>(fileDic->get("length"));
if(lengthData) {
length += lengthData->toLLInt();
} else {
throw new DlAbortEx(MSG_SOMETHING_MISSING_IN_TORRENT, "file length");
}
const List* pathList = dynamic_cast<const List*>(fileDic->get("path"));
if(!pathList) {
throw new DlAbortEx(MSG_SOMETHING_MISSING_IN_TORRENT, "file path list");
}
const MetaList& paths = pathList->getList();
string path;
for(int32_t i = 0; i < (int32_t)paths.size()-1; i++) {
Data* subpath = (Data*)paths[i];
path += subpath->toString()+"/";
const Data* subpath = dynamic_cast<const Data*>(paths[i]);
if(subpath) {
path += subpath->toString()+"/";
} else {
throw new DlAbortEx(MSG_SOMETHING_MISSING_IN_TORRENT, "file path element");
}
}
const Data* lastPath = dynamic_cast<const Data*>(paths.back());
if(lastPath) {
path += lastPath->toString();
} else {
throw new DlAbortEx(MSG_SOMETHING_MISSING_IN_TORRENT, "file path element");
}
// TODO use dynamic_cast
Data* lastPath = (Data*)paths.back();
path += lastPath->toString();
Strings uris;
transform(urlList.begin(), urlList.end(), back_inserter(uris),
@ -153,28 +169,37 @@ void DefaultBtContext::extractFileEntries(Dictionary* infoDic,
} else {
// single-file mode;
fileMode = BtContext::SINGLE;
Data* length = (Data*)infoDic->get("length");
totalLength = length->toLLInt();
const Data* length = dynamic_cast<const Data*>(infoDic->get("length"));
if(length) {
totalLength = length->toLLInt();
} else {
throw new DlAbortEx(MSG_SOMETHING_MISSING_IN_TORRENT, "file length");
}
FileEntryHandle fileEntry(new FileEntry(name, totalLength, 0, urlList));
fileEntries.push_back(fileEntry);
}
}
void DefaultBtContext::extractAnnounce(Data* announceData) {
void DefaultBtContext::extractAnnounce(const Data* announceData) {
Strings urls;
urls.push_back(Util::trim(announceData->toString()));
announceTiers.push_back(AnnounceTierHandle(new AnnounceTier(urls)));
}
void DefaultBtContext::extractAnnounceList(List* announceListData) {
void DefaultBtContext::extractAnnounceList(const List* announceListData) {
for(MetaList::const_iterator itr = announceListData->getList().begin();
itr != announceListData->getList().end(); itr++) {
const List* elem = (List*)*itr;
const List* elem = dynamic_cast<const List*>(*itr);
if(!elem) {
continue;
}
Strings urls;
for(MetaList::const_iterator elemItr = elem->getList().begin();
elemItr != elem->getList().end(); elemItr++) {
const Data* data = (Data*)*elemItr;
urls.push_back(Util::trim(data->toString()));
const Data* data = dynamic_cast<const Data*>(*elemItr);
if(data) {
urls.push_back(Util::trim(data->toString()));
}
}
if(urls.size()) {
AnnounceTierHandle tier(new AnnounceTier(urls));
@ -187,16 +212,16 @@ Strings DefaultBtContext::extractUrlList(const MetaEntry* obj)
{
Strings uris;
if(dynamic_cast<const List*>(obj)) {
const List* urlList = (const List*)obj;
const List* urlList = reinterpret_cast<const List*>(obj);
for(MetaList::const_iterator itr = urlList->getList().begin();
itr != urlList->getList().end(); ++itr) {
Data* data = dynamic_cast<Data*>(*itr);
const Data* data = dynamic_cast<const Data*>(*itr);
if(data) {
uris.push_back(data->toString());
}
}
} else if(dynamic_cast<const Data*>(obj)) {
const Data* urlData = (const Data*)obj;
const Data* urlData = reinterpret_cast<const Data*>(obj);
uris.push_back(urlData->toString());
}
return uris;
@ -204,27 +229,30 @@ Strings DefaultBtContext::extractUrlList(const MetaEntry* obj)
void DefaultBtContext::loadFromMemory(const char* content, int32_t length, const string& defaultName)
{
MetaEntry* rootEntry = MetaFileUtil::bdecoding(content, length);
if(!dynamic_cast<Dictionary*>(rootEntry)) {
SharedHandle<MetaEntry> rootEntry = MetaFileUtil::bdecoding(content, length);
const Dictionary* rootDic = dynamic_cast<const Dictionary*>(rootEntry.get());
if(!rootDic) {
throw new DlAbortEx("torrent file does not contain a root dictionary .");
}
processMetaInfo(rootEntry, defaultName);
processRootDictionary(rootDic, defaultName);
}
void DefaultBtContext::load(const string& torrentFile) {
MetaEntry* rootEntry = MetaFileUtil::parseMetaFile(torrentFile);
if(!dynamic_cast<Dictionary*>(rootEntry)) {
SharedHandle<MetaEntry> rootEntry = MetaFileUtil::parseMetaFile(torrentFile);
const Dictionary* rootDic = dynamic_cast<const Dictionary*>(rootEntry.get());
if(!rootDic) {
throw new DlAbortEx("torrent file does not contain a root dictionary .");
}
processMetaInfo(rootEntry, torrentFile);
processRootDictionary(rootDic, torrentFile);
}
void DefaultBtContext::processMetaInfo(const MetaEntry* rootEntry, const string& defaultName)
void DefaultBtContext::processRootDictionary(const Dictionary* rootDic, const string& defaultName)
{
clear();
SharedHandle<Dictionary> rootDic =
SharedHandle<Dictionary>((Dictionary*)rootEntry);
Dictionary* infoDic = (Dictionary*)rootDic->get("info");
const Dictionary* infoDic = dynamic_cast<const Dictionary*>(rootDic->get("info"));
if(!infoDic) {
throw new DlAbortEx(MSG_SOMETHING_MISSING_IN_TORRENT, "info directory");
}
// retrieve infoHash
ShaVisitor v;
infoDic->accept(&v);
@ -232,10 +260,16 @@ void DefaultBtContext::processMetaInfo(const MetaEntry* rootEntry, const string&
v.getHash(infoHash, len);
infoHashString = Util::toHex(infoHash, INFO_HASH_LENGTH);
// calculate the number of pieces
Data* pieceHashData = (Data*)infoDic->get("pieces");
const Data* pieceHashData = dynamic_cast<const Data*>(infoDic->get("pieces"));
if(!pieceHashData) {
throw new DlAbortEx(MSG_SOMETHING_MISSING_IN_TORRENT, "pieces");
}
numPieces = pieceHashData->getLen()/PIECE_HASH_LENGTH;
// retrieve piece length
Data* pieceLengthData = (Data*)infoDic->get("piece length");
const Data* pieceLengthData = dynamic_cast<const Data*>(infoDic->get("piece length"));
if(!pieceLengthData) {
throw new DlAbortEx(MSG_SOMETHING_MISSING_IN_TORRENT, "piece length");
}
pieceLength = pieceLengthData->toInt();
// retrieve piece hashes
extractPieceHash((unsigned char*)pieceHashData->getData(),
@ -253,9 +287,12 @@ void DefaultBtContext::processMetaInfo(const MetaEntry* rootEntry, const string&
Strings urlList = extractUrlList(rootDic->get("url-list"));
// retrieve file entries
extractFileEntries(infoDic, defaultName, urlList);
if((totalLength+pieceLength-1)/pieceLength != numPieces) {
throw new DlAbortEx("Too few/many piece hash.");
}
// retrieve announce
Data* announceData = (Data*)rootDic->get("announce");
List* announceListData = (List*)rootDic->get("announce-list");
const Data* announceData = dynamic_cast<const Data*>(rootDic->get("announce"));
const List* announceListData = dynamic_cast<const List*>(rootDic->get("announce-list"));
if(announceListData) {
extractAnnounceList(announceListData);
} else if(announceData) {

View File

@ -42,6 +42,7 @@
class Randomizer;
typedef SharedHandle<Randomizer> RandomizerHandle;
class Logger;
#define INFO_HASH_LENGTH 20
#define PIECE_HASH_LENGTH 20
@ -66,19 +67,21 @@ private:
RequestGroup* _ownerRequestGroup;
const Logger* _logger;
void clear();
void extractPieceHash(const unsigned char* hashData,
int32_t hashDataLength,
int32_t hashLength);
void extractFileEntries(Dictionary* infoDic,
void extractFileEntries(const Dictionary* infoDic,
const string& defaultName,
const Strings& urlList);
void extractAnnounce(Data* announceData);
void extractAnnounceList(List* announceListData);
void extractAnnounce(const Data* announceData);
void extractAnnounceList(const List* announceListData);
Strings extractUrlList(const MetaEntry* obj);
void processMetaInfo(const MetaEntry* rootEntry, const string& defaultName);
void processRootDictionary(const Dictionary* rootDic, const string& defaultName);
public:
DefaultBtContext();

View File

@ -135,6 +135,8 @@
#define MSG_TOO_SMALL_PAYLOAD_SIZE _("Too small payload size for %s, size=%d.")
#define MSG_REMOVED_DEFUNCT_CONTROL_FILE _("Removed the defunct control file %s because the download file %s doesn't exist.")
#define MSG_SHARE_RATIO_REPORT _("Your share ratio was %.1f, uploaded/downloaded=%sB/%sB")
#define MSG_SOMETHING_MISSING_IN_TORRENT _("Missing %s in torrent metainfo.")
#define MSG_NULL_TRACKER_RESPONSE _("Tracker returned null data.")
#define EX_TIME_OUT _("Timeout.")
#define EX_INVALID_CHUNK_SIZE _("Invalid chunk size.")

View File

@ -21,6 +21,9 @@ class DefaultBtAnnounceTest:public CppUnit::TestFixture {
CPPUNIT_TEST(testIsAllAnnounceFailed);
CPPUNIT_TEST(testURLOrderInStoppedEvent);
CPPUNIT_TEST(testURLOrderInCompletedEvent);
CPPUNIT_TEST(testProcessAnnounceResponse_malformed);
CPPUNIT_TEST(testProcessAnnounceResponse_failureReason);
CPPUNIT_TEST(testProcessAnnounceResponse);
CPPUNIT_TEST_SUITE_END();
private:
MockBtContextHandle _btContext;
@ -78,6 +81,9 @@ public:
void testIsAllAnnounceFailed();
void testURLOrderInStoppedEvent();
void testURLOrderInCompletedEvent();
void testProcessAnnounceResponse_malformed();
void testProcessAnnounceResponse_failureReason();
void testProcessAnnounceResponse();
};
@ -275,3 +281,47 @@ void DefaultBtAnnounceTest::testURLOrderInCompletedEvent()
btAnnounce.announceSuccess();
}
void DefaultBtAnnounceTest::testProcessAnnounceResponse_malformed()
{
try {
string res = "i123e";
DefaultBtAnnounce(new MockBtContext(), _option).processAnnounceResponse(res.c_str(), res.size());
CPPUNIT_FAIL("exception must be thrown.");
} catch(Exception* e) {
cerr << *e << endl;
delete e;
}
}
void DefaultBtAnnounceTest::testProcessAnnounceResponse_failureReason()
{
try {
string res = "d14:failure reason11:hello worlde";
DefaultBtAnnounce(new MockBtContext(), _option).processAnnounceResponse(res.c_str(), res.size());
CPPUNIT_FAIL("exception must be thrown.");
} catch(Exception* e) {
cerr << *e << endl;
delete e;
}
}
void DefaultBtAnnounceTest::testProcessAnnounceResponse()
{
string res = "d"
"15:warning message11:hello world"
"10:tracker id3:foo"
"8:intervali3000e"
"12:min intervali1800e"
"8:completei100e"
"10:incompletei200e"
"e";
DefaultBtAnnounce an(new MockBtContext(), _option);
an.processAnnounceResponse(res.c_str(), res.size());
CPPUNIT_ASSERT_EQUAL(string("foo"), an.getTrackerID());
CPPUNIT_ASSERT_EQUAL(3000, an.getInterval());
CPPUNIT_ASSERT_EQUAL(1800, an.getMinInterval());
CPPUNIT_ASSERT_EQUAL(100, an.getComplete());
CPPUNIT_ASSERT_EQUAL(200, an.getIncomplete());
}

View File

@ -29,6 +29,7 @@ class DefaultBtContextTest:public CppUnit::TestFixture {
CPPUNIT_TEST(testGetFileEntries_multiFileUrlList);
CPPUNIT_TEST(testGetFileEntries_singleFileUrlList);
CPPUNIT_TEST(testLoadFromMemory);
CPPUNIT_TEST(testLoadFromMemory_somethingMissing);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
@ -53,6 +54,7 @@ public:
void testGetFileEntries_multiFileUrlList();
void testGetFileEntries_singleFileUrlList();
void testLoadFromMemory();
void testLoadFromMemory_somethingMissing();
};
@ -325,3 +327,17 @@ void DefaultBtContextTest::testLoadFromMemory()
CPPUNIT_ASSERT_EQUAL(correctHash, Util::toHex(btContext.getInfoHash(),
btContext.getInfoHashLength()));
}
void DefaultBtContextTest::testLoadFromMemory_somethingMissing()
{
// pieces missing
try {
string memory = "d8:announce36:http://aria.rednoah.com/announce.php4:infod4:name13:aria2.tar.bz26:lengthi262144eee";
DefaultBtContext btContext;
btContext.loadFromMemory(memory.c_str(), memory.size(), "default");
CPPUNIT_FAIL("exception must be thrown.");
} catch(Exception* e) {
cerr << *e << endl;
delete e;
}
}

View File

@ -1 +1 @@
d8:url-list35:http://localhost/dist/aria2.tar.bz28:announce36:http://aria.rednoah.com/announce.php13:announce-listll15:http://tracker1el15:http://tracker2el15:http://tracker3ee7:comment17:REDNOAH.COM RULES13:creation datei1123456789e4:infod6:lengthi7680e4:name13:aria2.tar.bz212:piece lengthi128e6:pieces60:AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCee
d8:url-list35:http://localhost/dist/aria2.tar.bz28:announce36:http://aria.rednoah.com/announce.php13:announce-listll15:http://tracker1el15:http://tracker2el15:http://tracker3ee7:comment17:REDNOAH.COM RULES13:creation datei1123456789e4:infod6:lengthi380e4:name13:aria2.tar.bz212:piece lengthi128e6:pieces60:AAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCee