kerneltest/f32test/loader/security/t_fuzzldr.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 6 0173bcd7697c
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// f32test\loader\security\t_fuzzldr.cpp
// 
//

#define __E32TEST_EXTENSION__
#include <e32test.h>
#include <e32svr.h>
#include <e32uid.h>
#include <f32file.h>
#include <f32image.h>
#include "t_hash.h"

// Fuzzing parameters

const TInt KFuzzImages = 5;
const TInt KRandomFieldIterations = 8;
const TTimeIntervalMicroSeconds32 KDllTimeout = 10 * 1000000;

#define FUZZFIELD(OBJ, NAME) { (const TText*)(L ## #OBJ L"." L ## #NAME), sizeof(((OBJ*)8)->NAME), _FOFF(OBJ, NAME) }
#define DUMBFIELD(NAME, SIZE, OFFSET) { (const TText*)L ## NAME, SIZE, OFFSET }
#define FUZZEND { NULL, 0, 0 }

struct SFuzzField
	{
	const TText* name;
	TInt size;
	TInt offset;
	};

const SFuzzField HeaderFields[] =
	{
	FUZZFIELD(E32ImageHeaderV, iUid1),
	FUZZFIELD(E32ImageHeaderV, iUid2),
	FUZZFIELD(E32ImageHeaderV, iUid3),
	FUZZFIELD(E32ImageHeaderV, iUidChecksum),
	FUZZFIELD(E32ImageHeaderV, iSignature),
	FUZZFIELD(E32ImageHeaderV, iHeaderCrc),
	FUZZFIELD(E32ImageHeaderV, iModuleVersion),
	FUZZFIELD(E32ImageHeaderV, iCompressionType),
	FUZZFIELD(E32ImageHeaderV, iToolsVersion.iMajor),
	FUZZFIELD(E32ImageHeaderV, iToolsVersion.iMinor),
	FUZZFIELD(E32ImageHeaderV, iToolsVersion.iBuild),
	FUZZFIELD(E32ImageHeaderV, iTimeLo),
	FUZZFIELD(E32ImageHeaderV, iTimeHi),
	FUZZFIELD(E32ImageHeaderV, iFlags),
	FUZZFIELD(E32ImageHeaderV, iCodeSize),
	FUZZFIELD(E32ImageHeaderV, iDataSize),
	FUZZFIELD(E32ImageHeaderV, iHeapSizeMin),
	FUZZFIELD(E32ImageHeaderV, iHeapSizeMax),
	FUZZFIELD(E32ImageHeaderV, iStackSize),
	FUZZFIELD(E32ImageHeaderV, iBssSize),
	FUZZFIELD(E32ImageHeaderV, iEntryPoint),
	FUZZFIELD(E32ImageHeaderV, iCodeBase),
	FUZZFIELD(E32ImageHeaderV, iDataBase),
	FUZZFIELD(E32ImageHeaderV, iDllRefTableCount),
	FUZZFIELD(E32ImageHeaderV, iExportDirOffset),
	FUZZFIELD(E32ImageHeaderV, iExportDirCount),
	FUZZFIELD(E32ImageHeaderV, iTextSize),
	FUZZFIELD(E32ImageHeaderV, iCodeOffset),
	FUZZFIELD(E32ImageHeaderV, iDataOffset),
	FUZZFIELD(E32ImageHeaderV, iImportOffset),
	FUZZFIELD(E32ImageHeaderV, iCodeRelocOffset),
	FUZZFIELD(E32ImageHeaderV, iDataRelocOffset),
	FUZZFIELD(E32ImageHeaderV, iProcessPriority),
	FUZZFIELD(E32ImageHeaderV, iCpuIdentifier),
	FUZZFIELD(E32ImageHeaderV, iUncompressedSize),
	FUZZFIELD(E32ImageHeaderV, iS.iSecureId),
	FUZZFIELD(E32ImageHeaderV, iS.iVendorId),
	FUZZFIELD(E32ImageHeaderV, iS.iCaps.iCaps[0]),
	FUZZFIELD(E32ImageHeaderV, iS.iCaps.iCaps[1]),
	FUZZFIELD(E32ImageHeaderV, iExceptionDescriptor),
	FUZZFIELD(E32ImageHeaderV, iSpare2),
	FUZZFIELD(E32ImageHeaderV, iExportDescSize),
	FUZZFIELD(E32ImageHeaderV, iExportDescType),
	FUZZFIELD(E32ImageHeaderV, iExportDesc[0]),
	FUZZEND
	};

const SFuzzField ImportSectionFields[] =
	{
	FUZZFIELD(E32ImportSection, iSize),
	FUZZEND
	};

const SFuzzField ImportBlockFields[] =
	{
	FUZZFIELD(E32ImportBlock, iOffsetOfDllName), // we should fuzz the string also
	FUZZFIELD(E32ImportBlock, iNumberOfImports),
	FUZZEND
	};

const SFuzzField ImportEntryFields[] =
	{
	DUMBFIELD("import", 4, 0),
	FUZZEND
	};

const SFuzzField RelocSectionFields[] =
	{
	FUZZFIELD(E32RelocSection, iSize),
	FUZZFIELD(E32RelocSection, iNumberOfRelocs),
	FUZZEND
	};

const SFuzzField RelocBlockFields[] =
	{
	FUZZFIELD(E32RelocBlock, iPageOffset),
	FUZZFIELD(E32RelocBlock, iBlockSize),
	FUZZEND
	};

const SFuzzField RelocEntryFields[] =
	{
	DUMBFIELD("reloc", 2, 0),
	FUZZEND
	};

const SFuzzField ExportEntryFields[] =
	{
	DUMBFIELD("export", 4, 0),
	FUZZEND
	};

const SFuzzField CompressedDataFields[] =
	{
	DUMBFIELD("data1", 4, 0),
	DUMBFIELD("data2", 4, 4),
	DUMBFIELD("data3", 4, 16),
	DUMBFIELD("data4", 4, 64),
	DUMBFIELD("data5", 4, 256),
	DUMBFIELD("data6", 4, 1024),
	DUMBFIELD("data7", 4, 4096),
	FUZZEND
	};

// Values to try for different sizes, signed here but can be interpreted as either
// each will also try the smaller sizes' values
const TInt8 Values8[] = { KMinTInt8, -100, -10, -2, -1, 0, 1, 2, 10, 100, KMaxTInt8 };
const TInt Values8Count = sizeof(Values8)/sizeof(TInt8);
const TInt16 Values16[] = { KMinTInt16, -10000, -256, -255, 128, 255, 256, 10000, KMaxTInt16 };
const TInt Values16Count = sizeof(Values16)/sizeof(TInt16);
const TInt32 Values32[] = { KMinTInt32, -1000000000, -65536, -65535, 32768, 65535, 65536, 268435455, 268435456, 1000000000, KMaxTInt32 };
const TInt Values32Count = sizeof(Values32)/sizeof(TInt32);
const TInt ValuesCount[] = { 0, Values8Count, Values8Count+Values16Count, 0, Values8Count+Values16Count+Values32Count };
const TInt Offsets[] = { 1, 2, 4, 16, -1, -2, -4, -16 };
const TInt OffsetsCount = sizeof(Offsets)/sizeof(TInt);

// Regular boring definitions and stuff

RTest test(_L("T_FUZZLDR"));
RFs TheFs;
CFileMan* FileMan;

_LIT(KOrigDir, "Z:\\sys\\bin\\");
_LIT(KSysBin, ":\\sys\\bin\\");
_LIT(KSysHash, ":\\sys\\hash\\");
_LIT(KImageName, "fuzzv");
_LIT(KExeExt, ".exe");
_LIT(KDllExt, ".dll");
_LIT(KMyself, "t_fuzzldr");
_LIT(KSlaveArg, "-l ");

TFileName Provided;
TFileName Original;
TFileName Current;
TFileName Hash;
TFileName HashDir;
TBool LoadDll;
RFile File;
RTimer Timer;
TUint8* Target;
E32ImageHeaderV* Header;
E32ImageHeaderV* CleanHeader;
CSHA1* Hasher;

TInt FileSize;
TInt OutFileSize;
TUint8* CleanFileData;
TPtr8 CleanFileDes(CleanFileData, 0);
TUint8* FileData;
TPtr8 FileDes(FileData, 0);
TUint8* EndOfFile;
TChar Drive = '?', InternalDrive = '?', RemovableDrive = '?';

TBool Verbose;
TBool Forever = EFalse;
TUint32 Seed = 0;
typedef void (*TFieldFuzzer)(const SFuzzField*, TInt);

enum SetMode {
	ESetLiteral,
	ESetOffset,
	ESetRandom,
	ESetXor,
};

enum ValueMode {
	EValLiteral,
	EValOffset,
	EValRandom,
	EValXor,
	EValList,
	EValOffsetList,
};


TUint32 Rand()
	{
	Seed *= 69069;
	Seed += 5;
	return Seed;
	}


TUint32 Rand(TUint32 aLimit)
	{
	TUint64 temp = (TUint64)Rand() * (TUint64)aLimit;
	return (TUint32)(temp>>32);
	}


void PrepareName(TInt aNum, TBool aDll)
	{
	Original = KOrigDir;
	Original += KImageName;
	Original.AppendNum(aNum);
	Original += aDll ? KDllExt : KExeExt;
	Current.Zero();
	Current.Append(Drive);
	Current += KSysBin;
	Current += KImageName;
	Current.AppendNum(aNum);
	Current += aDll ? KDllExt : KExeExt;
	Hash = HashDir;
	Hash += KImageName;
	Hash.AppendNum(aNum);
	Hash += aDll ? KDllExt : KExeExt;
	}


void PrepareProvidedName()
	{
	Original = KOrigDir;
	Original += Provided;
	Current.Zero();
	Current.Append(Drive);
	Current += KSysBin;
	Current += Provided;
	Hash = HashDir;
	Hash += Provided;
	}


void MakeCleanCopy()
	{
	Mem::Copy(FileData, CleanFileData, FileSize);
	}


void LoadCleanFile()
	{
	test_KErrNone(File.Open(TheFs, Original, EFileRead));
	test_KErrNone(File.Size(FileSize));
	OutFileSize = FileSize;
	CleanFileData = new TUint8[FileSize];
	test_NotNull(CleanFileData);
	FileData = new TUint8[FileSize];
	EndOfFile = FileData + FileSize;
	test_NotNull(FileData);
	CleanFileDes.Set(CleanFileData, 0, FileSize);
	test_KErrNone(File.Read(CleanFileDes));
	File.Close();
	Header = (E32ImageHeaderV*)FileData;
	CleanHeader = (E32ImageHeaderV*)CleanFileData;
	FileDes.Set(FileData, FileSize, FileSize);
	MakeCleanCopy();
	test(CleanHeader->iUid1==(TUint32)KExecutableImageUidValue || CleanHeader->iUid1==(TUint32)KDynamicLibraryUidValue);
	LoadDll = CleanHeader->iUid1==(TUint32)KDynamicLibraryUidValue;
	}


void DoneFile()
	{
	delete[] FileData;
	delete[] CleanFileData;
	}


void RecalcChecksums()
	{
	if (Header->iUidChecksum == CleanHeader->iUidChecksum)
		{
		TUidType uids = *(const TUidType*)Header;
		TCheckedUid chkuid(uids);
		const TUint32* pChkUid = (const TUint32*)&chkuid;
		Header->iUidChecksum = pChkUid[3];
		}
	if (Header->iHeaderCrc == CleanHeader->iHeaderCrc)
		{
		Header->iHeaderCrc = KImageCrcInitialiser;
		TUint32 crc = 0;
		Mem::Crc32(crc, Header, sizeof(E32ImageHeaderV));
		Header->iHeaderCrc = crc;
		}
	}


void Load()
	{
	RecalcChecksums();
	test_KErrNone(File.Replace(TheFs, Current, EFileWrite));
	test_KErrNone(File.Write(FileDes, OutFileSize));
	test_KErrNone(File.Flush());
	File.Close();
	if (Drive == RemovableDrive)
		{
		TPtrC8 data(FileData, OutFileSize);
		Hasher->Reset();
		Hasher->Update(data);
		TBuf8<SHA1_HASH> hashVal = Hasher->Final();
		test_KErrNone(File.Replace(TheFs, Hash, EFileWrite));
		test_KErrNone(File.Write(hashVal));
		test_KErrNone(File.Flush());
		File.Close();
		}
	RProcess p;
	TInt r;
	if (LoadDll)
		{
		TFileName args;
		args.Copy(KSlaveArg);
		args.Append(Current);
		test_KErrNone(p.Create(KMyself, args));
		TRequestStatus logon, rendez, timeout;
		p.Logon(logon);
		p.Rendezvous(rendez);
		p.Resume();
		User::WaitForRequest(rendez);
		test(rendez==KErrNone);
		Timer.After(timeout, KDllTimeout);
		User::WaitForRequest(logon, timeout);
		if (logon == KRequestPending)
			{
			p.Kill(0);
			User::WaitForRequest(logon);
			}
		else
			{
			Timer.Cancel();
			User::WaitForRequest(timeout);
			}
		p.Close();
		// we don't check the return code as passing it back makes the log output
		// super spammy with KPANIC on - it prints for every nonzero return code.
		if (Verbose) test.Printf(_L("\n"));
		}
	else
		{
		r = p.Create(Current, KNullDesC);
		if (r==KErrNone)
			p.Kill(0);
		if (Verbose) test.Printf(_L("=> %d\n"), r);
		}
	p.Close();
	}


template <typename T> void SetFieldTo(const SFuzzField* aField, T aSetTo, SetMode aMode)
	{
	T* field = (T*)(Target + aField->offset);
	if ((TUint8*)field >= EndOfFile)
		{
		if (Verbose) test.Printf(_L("skipping, eof "));
		return;
		}
	if (aMode == ESetOffset)
		aSetTo += *field;
	else if (aMode == ESetRandom)
		aSetTo = (T)Rand();
	else if (aMode == ESetXor)
		aSetTo ^= *field;
	*field = aSetTo;
	if (Verbose) test.Printf(_L("%d "), aSetTo);
	}


void SetField(const SFuzzField* aField, TInt aValue, ValueMode aMode)
	{
	if (aMode < EValList)
		{
		switch(aField->size)
			{
		case 1:
			SetFieldTo<TInt8>(aField, aValue, (SetMode)aMode);
			break;
		case 2:
			SetFieldTo<TInt16>(aField, aValue, (SetMode)aMode);
			break;
		case 4:
			SetFieldTo<TInt32>(aField, aValue, (SetMode)aMode);
			break;
			}
		}
	else if (aMode == EValList)
		{
		switch(aField->size)
			{
		case 1:
			SetFieldTo<TInt8>(aField, Values8[aValue], ESetLiteral);
			break;
		case 2:
			if (aValue < ValuesCount[1])
				SetFieldTo<TInt16>(aField, Values8[aValue], ESetLiteral);
			else
				SetFieldTo<TInt16>(aField, Values16[aValue-ValuesCount[1]], ESetLiteral);
			break;
		case 4:
			if (aValue < ValuesCount[1])
				SetFieldTo<TInt32>(aField, Values8[aValue], ESetLiteral);
			else if (aValue < ValuesCount[2])
				SetFieldTo<TInt32>(aField, Values16[aValue-ValuesCount[1]], ESetLiteral);
			else
				SetFieldTo<TInt32>(aField, Values32[aValue-ValuesCount[2]], ESetLiteral);
			break;
			}
		}
	else if (aMode == EValOffsetList)
		{
		switch(aField->size)
			{
		case 1:
			SetFieldTo<TInt8>(aField, Offsets[aValue], ESetOffset);
			break;
		case 2:
			SetFieldTo<TInt16>(aField, Offsets[aValue], ESetOffset);
			break;
		case 4:
			SetFieldTo<TInt32>(aField, Offsets[aValue], ESetOffset);
			break;
			}
		}
	}


void FuzzFieldsDeterministically(const SFuzzField* aFields, TInt aOffset)
	{
	Target = FileData + aOffset;

	TInt f = -1;
   	while (aFields[++f].name)
		{
		test.Printf(_L("FIELD: %s ...\n"), aFields[f].name);
		TInt v;
		if (Verbose) test.Next(_L("Using preset values"));
		for (v = 0; v < ValuesCount[aFields[f].size]; ++v)
			{
			MakeCleanCopy();
			SetField(&aFields[f], v, EValList);
			Load();
			}
		if (Verbose) test.Next(_L("Using preset offsets"));
		for (v = 0; v < OffsetsCount; ++v)
			{
			MakeCleanCopy();
			SetField(&aFields[f], v, EValOffsetList);
			Load();
			}
		if (Verbose) test.Next(_L("Flipping single bits"));
		for (v = 0; v < aFields[f].size*8; ++v)
			{
			MakeCleanCopy();
			SetField(&aFields[f], 1<<v, EValXor);
			Load();
			}
		if (Verbose) test.Next(_L("Inverting"));
		MakeCleanCopy();
		SetField(&aFields[f], 0xffffffffu, EValXor);
		Load();

		// things that are offsets all go below, pointless on
		// narrow fields
		if (aFields[f].size == 4)
			{
			if (Verbose) test.Next(_L("Using filesize relative values"));
			for (v = FileSize-4; v <= FileSize+4; ++v)
				{
				MakeCleanCopy();
				SetField(&aFields[f], v, EValLiteral);
				Load();
				}
			if (Verbose) test.Next(_L("Using code-end relative values"));
			TInt codeend = CleanHeader->iCodeSize + CleanHeader->iCodeOffset;
			for (v = codeend-4; v <= codeend+4; ++v)
				{
				MakeCleanCopy();
				SetField(&aFields[f], v, EValLiteral);
				Load();
				}
			}
		}
	}


void FuzzFieldsRandomly(const SFuzzField* aFields, TInt aOffset)
	{
	Target = FileData + aOffset;

	TInt f = 0;
   	while (aFields[f].name)
		{
		test.Printf(_L("FIELD: %s ... (random)\n"), aFields[f].name);
		TInt v;
		for (v = 0; v < KRandomFieldIterations; ++v)
			{
			MakeCleanCopy();
			SetField(&aFields[f], 0, EValRandom);
			Load();
			}
		f++;
		}
	}


void FuzzBlockRandomly(TInt aOffset, TInt aSize)
	{
	SFuzzField field;
	field.size = 1;
	Target = FileData + aOffset;
	
	test.Printf(_L("FIELD: random words in data\n"));
	TInt v;
	for (v = 0; v < KRandomFieldIterations * 4; ++v)
		{
		MakeCleanCopy();
		field.offset = Rand(aSize);
		if (Verbose) test.Printf(_L("@ %d, "), field.offset);
		SetField(&field, 0, EValRandom);
		Load();
		}
	}


void FuzzFile(TBool aRandom)
	{
	TTime before, after;
	before.UniversalTime();
	LoadCleanFile();
	
	TFieldFuzzer FuzzFields = aRandom ? FuzzFieldsRandomly : FuzzFieldsDeterministically;

	// E32ImageHeader
	FuzzFields(HeaderFields, 0);

	if (CleanHeader->iCompressionType == KFormatNotCompressed)
		{
		// import table
		TInt offset = CleanHeader->iImportOffset;
		if (offset != 0)
			{
			FuzzFields(ImportSectionFields, offset);
			offset += sizeof(E32ImportSection);
			FuzzFields(ImportBlockFields, offset);
			offset += sizeof(E32ImportBlock);
			FuzzFields(ImportEntryFields, offset);
			}

		// code relocations
		offset = CleanHeader->iCodeRelocOffset;
		if (offset != 0)
			{
			FuzzFields(RelocSectionFields, offset);
			offset += sizeof(E32RelocSection);
			FuzzFields(RelocBlockFields, offset);
			offset += sizeof(E32RelocBlock);
			FuzzFields(RelocEntryFields, offset);
			}

		// data relocations
		offset = CleanHeader->iDataRelocOffset;
		if (offset != 0)
			{
			FuzzFields(RelocSectionFields, offset);
			offset += sizeof(E32RelocSection);
			FuzzFields(RelocBlockFields, offset);
			offset += sizeof(E32RelocBlock);
			FuzzFields(RelocEntryFields, offset);
			}

		// export table
		offset = CleanHeader->iExportDirOffset;
		if (offset != 0)
			{
			FuzzFields(ExportEntryFields, offset);
			}
		}
	else
		{
		if (aRandom)
			{
			// random bits of the compressed data
			FuzzBlockRandomly(CleanHeader->iCodeOffset, FileSize - CleanHeader->iCodeOffset);
			}
		else
			{
			// arbitrary bits of the compressed data
			FuzzFields(CompressedDataFields, CleanHeader->iCodeOffset);
			}
		}

	DoneFile();
	after.UniversalTime();
	TTimeIntervalSeconds interval;
	after.SecondsFrom(before, interval);
	test.Printf(_L("Took %d seconds\n"), interval.Int());
	}


void FuzzTruncateTo(TInt size)
	{
	OutFileSize = size - 4;
	if (Verbose) test.Printf(_L("%d "), OutFileSize);
	Load();
	OutFileSize = size - 1;
	if (Verbose) test.Printf(_L("%d "), OutFileSize);
	Load();
	if (size == FileSize)
		return;
	OutFileSize = size;
	if (Verbose) test.Printf(_L("%d "), OutFileSize);
	Load();
	OutFileSize = size + 1;
	if (Verbose) test.Printf(_L("%d "), OutFileSize);
	Load();
	OutFileSize = size + 4;
	if (Verbose) test.Printf(_L("%d "), OutFileSize);
	Load();
	}


void FuzzTruncate()
	{
	TTime before, after;
	before.UniversalTime();
	LoadCleanFile();

	FuzzTruncateTo(CleanHeader->iCodeOffset);
	if (CleanHeader->iCompressionType == KFormatNotCompressed)
		FuzzTruncateTo(CleanHeader->iCodeOffset+CleanHeader->iCodeSize);
	FuzzTruncateTo(FileSize);

	DoneFile();
	after.UniversalTime();
	TTimeIntervalSeconds interval;
	after.SecondsFrom(before, interval);
	test.Printf(_L("Took %d seconds\n"), interval.Int());
	}


void FuzzAllTestImages()
	{
	TInt i;
	Drive = InternalDrive;
	test.Next(_L("Fuzzing deterministically"));
	for (i=1; i<=KFuzzImages; ++i)
		{
		test.Next(_L("Next binary..."));
		test.Printf(_L("Fuzzing exe %d\n"), i);
		PrepareName(i, EFalse);
		FuzzFile(EFalse);
		if(i==5)
			continue; // DLL 5 doesn't exist because toolchain doesn't like DLLs with no exports
		test.Next(_L("Next binary..."));
		test.Printf(_L("Fuzzing dll %d\n"), i);
		PrepareName(i, ETrue);
		FuzzFile(EFalse);
		}
	Drive = RemovableDrive;
	test.Next(_L("Fuzzing deterministically on removable media"));
	for (i=1; i<=KFuzzImages; ++i)
		{
		test.Next(_L("Next binary..."));
		test.Printf(_L("Fuzzing exe %d\n"), i);
		PrepareName(i, EFalse);
		FuzzFile(EFalse);
		if(i==5)
			continue; // DLL 5 doesn't exist because toolchain doesn't like DLLs with no exports
		test.Next(_L("Next binary..."));
		test.Printf(_L("Fuzzing dll %d\n"), i);
		PrepareName(i, ETrue);
		FuzzFile(EFalse);
		}
	Drive = InternalDrive;
	test.Next(_L("Fuzzing by truncation"));
	for (i=1; i<=KFuzzImages; ++i)
		{
		test.Next(_L("Next binary..."));
		test.Printf(_L("Fuzzing exe %d\n"), i);
		PrepareName(i, EFalse);
		FuzzTruncate();
		if(i==5)
			continue; // DLL 5 doesn't exist because toolchain doesn't like DLLs with no exports
		test.Next(_L("Next binary..."));
		test.Printf(_L("Fuzzing dll %d\n"), i);
		PrepareName(i, ETrue);
		FuzzTruncate();
		}	
	Drive = RemovableDrive;
	test.Next(_L("Fuzzing by truncation on removable media"));
	for (i=1; i<=KFuzzImages; ++i)
		{
		test.Next(_L("Next binary..."));
		test.Printf(_L("Fuzzing exe %d\n"), i);
		PrepareName(i, EFalse);
		FuzzTruncate();
		if(i==5)
			continue; // DLL 5 doesn't exist because toolchain doesn't like DLLs with no exports
		test.Next(_L("Next binary..."));
		test.Printf(_L("Fuzzing dll %d\n"), i);
		PrepareName(i, ETrue);
		FuzzTruncate();
		}	
	test.Next(_L("Fuzzing randomly"));
	do
		{
		for (i=1; i<=KFuzzImages; ++i)
			{
			Drive = InternalDrive;
			test.Next(_L("Next binary..."));
			test.Printf(_L("Fuzzing exe %d\n"), i);
			PrepareName(i, EFalse);
			FuzzFile(ETrue);
			if(i==5)
				continue; // DLL 5 doesn't exist because toolchain doesn't like DLLs with no exports
			test.Next(_L("Next binary..."));
			test.Printf(_L("Fuzzing dll %d\n"), i);
			PrepareName(i, ETrue);
			FuzzFile(ETrue);
			Drive = RemovableDrive;
			test.Next(_L("Next binary..."));
			test.Printf(_L("Fuzzing exe %d on removable media\n"), i);
			PrepareName(i, EFalse);
			FuzzFile(ETrue);
			if(i==5)
				continue; // DLL 5 doesn't exist because toolchain doesn't like DLLs with no exports
			test.Next(_L("Next binary..."));
			test.Printf(_L("Fuzzing dll %d on removable media\n"), i);
			PrepareName(i, ETrue);
			FuzzFile(ETrue);
			}
		}
	while (Forever);
	}


void FuzzProvidedImage()
	{
	test.Printf(_L("Fuzzing file %S\n"), &Provided);
	PrepareProvidedName();
	Drive = InternalDrive;
	test.Next(_L("Fuzzing deterministically"));
	FuzzFile(EFalse);
	Drive = RemovableDrive;
	test.Next(_L("Fuzzing deterministically on removable media"));
	FuzzFile(EFalse);
	test.Next(_L("Fuzzing by truncation"));
	FuzzTruncate();
	Drive = RemovableDrive;
	test.Next(_L("Fuzzing by truncation on removable media"));
	FuzzTruncate();
	test.Next(_L("Fuzzing randomly"));
	do
		{
		Drive = InternalDrive;
		test.Next(_L("Internal drive"));
		FuzzFile(ETrue);
		Drive = RemovableDrive;
		test.Next(_L("Removable drive"));
		FuzzFile(ETrue);
		}
	while (Forever);
	}


GLDEF_C TInt E32Main()
	{
	// default to verbose unless the fasttest trace flag is on
	Verbose = (UserSvr::DebugMask(2)&0x00000002) == 0;

	TFileName cmd;
	User::CommandLine(cmd);
	TLex lex(cmd);
	FOREVER
		{
		lex.SkipSpace();
		if (lex.Eos())
			break;
		TChar next = lex.Peek();
		if (next == '-' || next == '/')
			{
			// option
			lex.Inc();
			switch(lex.Get())
				{
			case 'v':
				Verbose = ETrue;
				break;
			case 'q':
				Verbose = EFalse;
				break;
			case 'l':
				{
				// being used as a slave to load a DLL
				TPtrC libname(lex.NextToken());
				RLibrary l;
				RProcess::Rendezvous(KErrNone);
				l.Load(libname);
				return KErrNone;
				}
			case 's':
				// random seed
				lex.SkipSpace();
				test_KErrNone(lex.Val(Seed, EHex));
				test.Printf(_L("Using supplied random seed %08x\n"), Seed);
				break;
			case 'f':
				// run forever
				Forever = ETrue;
				break;
				}
			}
		else
			{
			// filename, at least i assume it is :)
			Provided.Copy(lex.NextToken());
			}
		}

	test.Title();
	test.Next(_L("Setup"));
	__UHEAP_MARK;
	CTrapCleanup* cleanup;
	cleanup=CTrapCleanup::New();

	if (Seed == 0)
		{
		TTime time;
		time.UniversalTime();
		Seed = (TUint32)time.Int64();
		test.Printf(_L("Random seed is %08x\n"), Seed);
		}

	test_KErrNone(TheFs.Connect());
	test_TRAP(FileMan=CFileMan::NewL(TheFs));
	test_KErrNone(Timer.CreateLocal());
	test_TRAP(Hasher=CSHA1::NewL());
	HashDir.Append(TheFs.GetSystemDriveChar());
	HashDir.Append(KSysHash);
	TInt r = TheFs.MkDirAll(HashDir);
	test(r == KErrNone || r == KErrAlreadyExists);	

	// Find some interesting drives
	for (TInt driveno = EDriveA; driveno <= EDriveZ; ++driveno)
		{
		TDriveInfo di;
		test_KErrNone(TheFs.Drive(di, driveno));
		if (di.iType == EMediaNotPresent)
			continue;
		TChar drivechar;
		test_KErrNone(TheFs.DriveToChar(driveno, drivechar));
		if ((di.iDriveAtt & KDriveAttInternal) && InternalDrive == '?')
			InternalDrive = drivechar;
		else if ((di.iDriveAtt & KDriveAttRemovable) && RemovableDrive == '?')
			RemovableDrive = drivechar;
		else
			continue;

		TFileName fn;
		fn.Append(drivechar);
		fn.Append(KSysBin);
		TheFs.MkDirAll(fn);
		test(r == KErrNone || r == KErrAlreadyExists);	
		}
	test.Printf(_L("Using %c as internal drive, %c as removable\n"), (TUint)InternalDrive, (TUint)RemovableDrive);

	// Turn off evil lazy dll unloading
	RLoader l;
	test_KErrNone(l.Connect());
	test_KErrNone(l.CancelLazyDllUnload());
	l.Close();

	test.Start(_L("Fuzzing loader"));
	if (Provided.Length() == 0)
		FuzzAllTestImages();
	else
		FuzzProvidedImage();
	test.End();

	delete Hasher;
	Timer.Close();
	delete FileMan;
	TheFs.Close();
	test.Close();
	delete cleanup;
	__UHEAP_MARKEND;
	return KErrNone;
	}