persistentstorage/sql/TEST/t_sqlbadclient.cpp
changeset 0 08ec8eefde2f
child 12 6b6fd149daa2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sql/TEST/t_sqlbadclient.cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,630 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "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:
+//
+
+#include <e32test.h>
+#include <bautils.h>
+#include <e32math.h>
+#include <sqldb.h>
+#include "SqlUtil.h"
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+static RFs TheFs;
+RTest TheTest(_L("t_sqlbadclient test"));
+_LIT(KTestDir, "c:\\test\\");
+_LIT(KTestDbName1, "c:\\test\\t_sqlbadclient.db");
+_LIT(KTestDbName2, "c:[1111D1C1]t_sqlbadclient.db");
+
+#if defined __WINS__ || defined __WINSCW__
+const TInt KTestIterCount = 5000;
+#else
+const TInt KTestIterCount = 4000;
+#endif
+const TInt KMaxDesArgLen = 1000;
+enum TArgType 
+	{
+	EIntArgType, 
+	ETextArgType, 
+	EBinArgType, 
+	ELastArgType
+	};
+
+//////////////////////////////////////////////////////////////////////////////////////
+
+//If the SQL server crashes and the test receives KErrServerTerminated error, then the 
+//next set will contain the last:
+// - iteration number;
+// - handle type;
+// - function code;
+// - handle;
+// - IPC arguments values;
+struct TThreadData
+	{
+	TInt				iIteration;
+	TSqlSrvHandleType 	iHandleType;
+	TInt 				iFunction;
+	TInt 				iHandle;
+	TArgType 			iArgType[KMaxMessageArguments];
+	TInt 				iIntArg[KMaxMessageArguments];
+	TBuf<KMaxDesArgLen> iTextArg[KMaxMessageArguments];
+	TBuf8<KMaxDesArgLen> iBinArg[KMaxMessageArguments];
+	TInt64 				iSeed;
+	};
+//////////////////////////////////////////////////////////////////////////////////////
+
+_LIT(KPanicCategory, "SrvTerm");
+_LIT(KPanicCategory2, "InvArg");
+const TInt KPanicCode = 1111;
+const TInt KPanicCode2 = 2222;
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+//Deletes all created test files.
+void DeleteTestFiles()
+	{
+	RSqlDatabase::Delete(KTestDbName1);
+	}
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//Test macros and functions
+void Check1(TInt aValue, TInt aLine, TBool aPrintThreadName = EFalse)
+	{
+	if(!aValue)
+		{
+		DeleteTestFiles();
+		if(aPrintThreadName)
+			{
+			RThread th;
+			TName name = th.Name();
+			RDebug::Print(_L("*** Thread %S, Line %d\r\n"), &name, aLine);
+			}
+		else
+			{
+			RDebug::Print(_L("*** Line %d\r\n"), aLine);
+			}
+		TheTest(EFalse, aLine);
+		}
+	}
+void Check2(TInt aValue, TInt aExpected, TInt aLine, TBool aPrintThreadName = EFalse)
+	{
+	if(aValue != aExpected)
+		{
+		DeleteTestFiles();
+		if(aPrintThreadName)
+			{
+			RThread th;
+			TName name = th.Name();
+			RDebug::Print(_L("*** Thread %S, Line %d Expected error: %d, got: %d\r\n"), &name, aLine, aExpected, aValue);
+			}
+		else
+			{
+			RDebug::Print(_L("*** Line %d, Expected error: %d, got: %d\r\n"), aLine, aExpected, aValue);
+			}
+		TheTest(EFalse, aLine);
+		}
+	}
+#define TEST(arg) ::Check1((arg), __LINE__)
+#define TEST2(aValue, aExpected) ::Check2(aValue, aExpected, __LINE__)
+#define TTEST(arg) ::Check1((arg), __LINE__, ETrue)
+#define TTEST2(aValue, aExpected) ::Check2(aValue, aExpected, __LINE__, ETrue)
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+//Creates file session instance and the test directory
+void CreateTestEnv()
+    {
+	TInt err = TheFs.Connect();
+	TEST2(err, KErrNone);
+
+	err = TheFs.MkDir(KTestDir);
+	TEST(err == KErrNone || err == KErrAlreadyExists);
+	}
+
+//Starts the SQL server process.
+TInt StartSqlServer()
+	{
+	const TUid KSqlSrvUid3 = {0x10281E17};//The same UID is in SqlSrv.mmp file
+	const TUidType serverUid(KNullUid, KNullUid, KSqlSrvUid3);
+	_LIT(KSqlSrvImg, "SqlSrv");//SQL server image name
+	RProcess server;
+	TInt err = server.Create(KSqlSrvImg, KNullDesC, serverUid);
+	if(err != KErrNone)
+		{
+		return err;
+		}
+	TRequestStatus stat;
+	server.Rendezvous(stat);
+	if(stat != KRequestPending)
+		{
+		server.Kill(0);		// abort startup
+		}
+	else
+		{
+		server.Resume();	// logon OK - start the server
+		}
+	User::WaitForRequest(stat);		// wait for start or death
+	// we can't use the 'exit reason' if the server panicked as this
+	// is the panic 'reason' and may be '0' which cannot be distinguished
+	// from KErrNone
+	err = (server.ExitType() == EExitPanic) ? KErrGeneral : stat.Int();
+	server.Close();
+	return err;
+	}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////        RTestSqlDbSession          //////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+//Test client SQL session class
+class RTestSqlDbSession : public RSessionBase
+	{
+public:	
+	TInt Connect();
+	void Close();
+	TInt SendReceive(TInt aFunction);
+	TInt SendReceive(TInt aFunction, const TIpcArgs& aArgs);
+
+private:
+	TInt DoCreateSession();
+	};
+	
+TInt RTestSqlDbSession::Connect()
+	{
+	TInt err = DoCreateSession();
+	if(err == KErrNone)
+		{
+		TIpcArgs ipcArgs(KTestDbName1().Length(), &KTestDbName1(), 0, 0);
+		err = SendReceive(ESqlSrvDbOpen, ipcArgs);
+		}
+	if(err != KErrNone && err != KErrAlreadyExists)
+		{
+		Close();	
+		}
+	return err;
+	}
+
+void RTestSqlDbSession::Close()
+	{
+	if(Handle())
+		{
+		(void)SendReceive(ESqlSrvDbClose);
+		}
+	RSessionBase::Close();
+	}
+
+TInt RTestSqlDbSession::SendReceive(TInt aFunction)
+	{
+	return RSessionBase::SendReceive(aFunction);	
+	}
+	
+TInt RTestSqlDbSession::SendReceive(TInt aFunction, const TIpcArgs& aArgs)
+	{
+	return RSessionBase::SendReceive(aFunction, aArgs);	
+	}
+
+TInt RTestSqlDbSession::DoCreateSession()
+	{
+	const TInt KTimesToRetryConnection = 2;
+	TInt retry = KTimesToRetryConnection;
+	_LIT(KSqlSrvName, "!SQL Server");//SqlDb server name
+	//SQL server: major version number, minor version number, build number constants.
+	const TInt KSqlMajorVer = 1;
+	const TInt KSqlMinorVer = 1;
+	const TInt KSqlBuildVer = 0;
+	for(;;)
+		{
+		TInt err = CreateSession(KSqlSrvName, TVersion(KSqlMajorVer, KSqlMinorVer, KSqlBuildVer));
+		if(err != KErrNotFound && err != KErrServerTerminated)
+			{
+			return err;
+			}
+		if(--retry == 0)
+			{
+			return err;
+			}
+		err = ::StartSqlServer();
+		if(err != KErrNone && err != KErrAlreadyExists)
+			{
+			return err;
+			}
+		}
+	}
+	
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+TInt SendReceive(RTestSqlDbSession aSession, TSqlSrvHandleType aHandleType, TInt aFunction, TInt aHandle, TIpcArgs& aArgs)
+	{
+	return aSession.SendReceive(::MakeMsgCode(static_cast <TSqlSrvFunction> (aFunction), aHandleType, aHandle), aArgs);
+	}
+
+TInt SendReceive(RTestSqlDbSession aSession, TInt aFunction, TIpcArgs& aArgs)
+	{
+	return aSession.SendReceive(aFunction, aArgs);
+	}
+
+void PrintIterationCount(TInt aIteration)
+	{
+	if((aIteration % 100) == 0)
+		{
+		TTime time;
+		time.HomeTime();
+		TDateTime dt = time.DateTime();
+		TBuf<16> tbuf;
+		tbuf.Format(_L("%02d:%02d:%02d.%06d"), dt.Hour(), dt.Minute(), dt.Second(), dt.MicroSecond());
+		RDebug::Print(_L("-----[%S] Test iterations: %d\r\n"), &tbuf, aIteration);
+		}
+	}
+
+//Worker thread function.
+//It behaves as a malicious client. Connects to the SQL server. In each test iteration generates some random values
+//for the function number, handle, handle type, IPC arguments. Then sends a command to the server using these
+//randomly generated values. If the server crashes and the thread function receives KErrServerTerminated error,
+//then the thread kills itself and the main thread will get KPanicCategory and KPanicCode as a reason for the
+//worker thread's death. The last set of randomly generated values will be stored in the memory, pointed by aData argument.
+TInt ThreadFunc1(void* aData)
+	{
+	__UHEAP_MARK;
+	
+	CTrapCleanup* tc = CTrapCleanup::New();
+	TTEST(tc != NULL);
+
+	TThreadData* p = static_cast <TThreadData*> (aData);
+	TTEST(p != NULL);
+	TThreadData& data = *p;
+
+	RTestSqlDbSession sess;
+	TInt err = sess.Connect();
+	TTEST2(err, KErrNone);
+
+	while(++data.iIteration <= KTestIterCount)
+		{
+		PrintIterationCount(data.iIteration);
+		TIpcArgs args;
+		do
+			{
+			data.iFunction = Math::Rand(data.iSeed) % (ESqlSrvStreamClose + 1);//ESqlSrvStreamClose - the last server message number)
+			}
+		while(data.iFunction >= ESqlSrvResourceMark && data.iFunction <= ESqlSrvSetHeapFailure);
+		for(TInt i=0;i<KMaxMessageArguments;++i)
+			{
+			//Initialize arguments
+			data.iArgType[i] = static_cast <TArgType> (Math::Rand(data.iSeed) % ELastArgType);
+			switch(data.iArgType[i])
+				{
+				case EIntArgType:
+					data.iIntArg[i] = Math::Rand(data.iSeed) % 9711;
+					args.Set(i, data.iIntArg[i]);
+					break;
+				case ETextArgType:
+					{
+					TInt len = Math::Rand(data.iSeed) % KMaxDesArgLen;	
+					data.iTextArg[i].SetLength(len);
+					args.Set(i, &data.iTextArg[i]);
+					}
+					break;
+				case EBinArgType:
+					{
+					TInt len = Math::Rand(data.iSeed) % KMaxDesArgLen;	
+					data.iBinArg[i].SetLength(len);
+					args.Set(i, &data.iBinArg[i]);
+					}
+					break;
+				default:
+					User::Panic(KPanicCategory2, KPanicCode2);
+					break;
+				}
+			}
+		//Send arguments
+		User::SetJustInTime(EFalse);
+		TInt err = KErrNone;
+		if((Math::Rand(data.iSeed) % 5) == 0)	//Pass a handle (statement or stream)
+			{
+			data.iHandleType = (Math::Rand(data.iSeed) % 2) ? ESqlSrvStatementHandle : ESqlSrvStreamHandle;
+			data.iHandle = Math::Rand(data.iSeed) % 64;
+			err = ::SendReceive(sess, data.iHandleType, data.iFunction, data.iHandle, args);
+			}
+		else
+			{
+			err = ::SendReceive(sess, data.iFunction, args);
+			}
+		if(err == KErrServerTerminated)
+			{
+			User::Panic(KPanicCategory, KPanicCode);
+			}
+		User::SetJustInTime(ETrue);
+		}
+
+	sess.Close();
+
+	delete tc;	
+	
+	__UHEAP_MARKEND;
+	
+	return KErrNone;		
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-CT-1769
+@SYMTestCaseDesc		In a loop, where the loop iterations are less than KTestIterCount (5000 at the moment), 
+						the test creates a public shared database and a worker thread, which will behave as 
+						malicious client. If the worker thread crashes the SQL server, then the worker thread
+						dies notifying the main thread about the SQL server crash. The main thread prints the 
+						values used in the last IPC call and crashes the test.
+@SYMTestPriority		High
+@SYMTestActions			SQL, Malicious client simulation test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ5792
+                        REQ5793
+						REQ10405
+						REQ10407
+*/	
+void BadClientTest()
+	{
+	TThreadData* p = new TThreadData;
+	TEST(p != NULL);
+	TThreadData& data = *p;
+	data.iFunction = 0;
+	TTime now;
+	now.UniversalTime();
+	data.iSeed = now.Int64();
+	
+	_LIT(KThreadName, "WorkThrd");
+	
+	for(data.iIteration=0;data.iIteration<KTestIterCount;++data.iIteration)
+		{
+		PrintIterationCount(data.iIteration);
+		//Create/open the test database
+		RSqlDatabase db;
+		TInt err = db.Create(KTestDbName1);
+		TEST(err == KErrNone || err == KErrAlreadyExists);
+		if(err == KErrNone)
+			{
+			err = db.Exec(_L8("CREATE TABLE A(Id INTEGER); INSERT INTO A(Id) VALUES(1); INSERT INTO A(Id) VALUES(2);"));	
+			TEST(err >= 0);
+			}
+		db.Close();
+		//Run the malicious client (one worker theread which will try to crash the SQL server)
+		RThread thread;
+		TEST2(thread.Create(KThreadName, &ThreadFunc1, 0x2000, 0x1000, 0x10000, &data, EOwnerProcess), KErrNone);
+		TRequestStatus status;
+		thread.Logon(status);
+		TEST2(status.Int(), KRequestPending);
+		thread.Resume();
+		User::WaitForRequest(status);
+		User::SetJustInTime(ETrue);	// enable debugger panic handling
+		if(thread.ExitType() == EExitPanic)
+			{
+			if(thread.ExitReason() == KPanicCode)
+				{
+				TheTest.Printf(_L("##Server terminated!\r\n"));
+				TheTest.Printf(_L("##Iteration=%d, Handle type(hex)=%X, Function(hex)=%X, Handle=%d\r\n"), data.iIteration, data.iHandleType, data.iFunction, data.iHandle);
+				for(TInt i=0;i<KMaxMessageArguments;++i)
+					{
+					switch(data.iArgType[i])
+						{
+						case EIntArgType:
+							TheTest.Printf(_L("##Arg %d, Integer, value=%d\r\n"), i, data.iIntArg[i]);
+							break;
+						case ETextArgType:
+							TheTest.Printf(_L("##Arg %d, Text,    length=%d\r\n"), i, data.iTextArg[i].Length());
+							break;
+						case EBinArgType:
+							TheTest.Printf(_L("##Arg %d, Binary,  length=%d\r\n"), i, data.iBinArg[i].Length());
+							break;
+						default:
+							TheTest.Printf(_L("##Arg %d, Invalid argument type: %d\r\n"), i, data.iArgType[i]);
+							break;
+						}
+					}
+				TEST(0);
+				}
+			}
+		thread.Close();
+		}
+	User::SetJustInTime(ETrue);	// enable debugger panic handling
+	delete p;
+	}
+
+RSqlSecurityPolicy CreateSecurityPolicy()
+	{
+	RSqlSecurityPolicy securityPolicy;
+	const TSecurityPolicy KDefaultPolicy(TSecurityPolicy::EAlwaysPass);
+	TInt err = securityPolicy.Create(KDefaultPolicy);
+	TEST2(err, KErrNone);
+	return securityPolicy;
+	}
+
+void DoBadNameTest(RSqlDatabase& aDb)
+	{
+	TFileName invalidFileName1;
+	invalidFileName1.SetLength(KMaxFileName);
+	//Initialise the first character so that it is not treated as an empty descriptor by the sever
+	invalidFileName1[0] = TChar(204); //0xCC
+	TFileName invalidFileName2;
+	invalidFileName2.SetLength(KMaxFileName);
+	invalidFileName2.Fill(TChar(204));//0xCC
+	
+	TInt err = aDb.Attach(invalidFileName1, invalidFileName2);
+	TheTest.Printf(_L("Attach, invalid database name-1, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+	err = aDb.Attach(invalidFileName2, invalidFileName1);
+	TheTest.Printf(_L("Attach, invalid database name-2, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+	err = aDb.Attach(KTestDbName2, invalidFileName2);
+	TheTest.Printf(_L("Attach, invalid database name-3, err=%d\r\n"), err);
+	TEST(err != KErrDied);
+	if(err == KErrNone)
+		{
+		err = aDb.Detach(invalidFileName2);
+		TheTest.Printf(_L("Detach, invalid database name-3, err=%d\r\n"), err);
+		TEST2(err, KErrNone);
+		}
+	err = aDb.Attach(KTestDbName2, invalidFileName1);
+	TheTest.Printf(_L("Attach, invalid database name-4, err=%d\r\n"), err);
+	TEST(err != KErrDied);
+	if(err == KErrNone)
+		{
+		err = aDb.Detach(invalidFileName1);
+		TheTest.Printf(_L("Detach, invalid database name-4, err=%d\r\n"), err);
+		TEST2(err, KErrNone);
+		}
+	
+	RSqlDatabase::TSize size;
+
+	err = aDb.Size(size, invalidFileName1);
+	TheTest.Printf(_L("Size, invalid database name-1, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+	err = aDb.Size(size, invalidFileName2);
+	TheTest.Printf(_L("Size, invalid database name-2, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+
+	err = aDb.Compact(RSqlDatabase::EMaxCompaction, invalidFileName1);
+	TheTest.Printf(_L("Compact, invalid database name-1, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+	err = aDb.Compact(RSqlDatabase::EMaxCompaction, invalidFileName2);
+	TheTest.Printf(_L("Compact, invalid database name-2, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+
+	RSqlStatement stmt;
+	err = stmt.Prepare(aDb, _L("SELECT * FROM A"));
+	TEST2(err, KErrNone);
+	
+	err = stmt.ColumnIndex(invalidFileName1);
+	TheTest.Printf(_L("ColumnIndex, invalid column name-1, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+	err = stmt.ColumnIndex(invalidFileName2);
+	TheTest.Printf(_L("ColumnIndex, invalid column name-2, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+	err = stmt.ParameterIndex(invalidFileName1);
+	TheTest.Printf(_L("ParameterIndex, invalid parameter name-1, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+	err = stmt.ParameterIndex(invalidFileName2);
+	TheTest.Printf(_L("ParameterIndex, invalid parameter name-2, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+
+	stmt.Close();
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4048
+@SYMTestCaseDesc		Bad database names - robustness test.
+						The test defines file names with invalid content and attempts to use 
+						these file names with the SQL API. The test should not cause crashes 
+						in the SQL server.
+@SYMTestPriority		High
+@SYMTestActions			Bad database names - robustness test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10405
+                        REQ10407
+*/
+void BadNameTest()
+	{
+	TFileName invalidFileName1;
+	invalidFileName1.SetLength(KMaxFileName);
+	TFileName invalidFileName2;
+	invalidFileName2.SetLength(KMaxFileName / 2);
+	
+	//Initialise the first character so that it is not treated as an empty descriptor by the sever
+	invalidFileName1[0] = TChar(204); //0xCC
+	invalidFileName2[0] = TChar(204); //0xCC
+	
+	(void)RSqlDatabase::Delete(KTestDbName1);
+	(void)RSqlDatabase::Delete(KTestDbName2);
+		
+	RSqlDatabase db;
+	TInt err = db.Create(invalidFileName1);
+	TEST(err != KErrNone);
+	err = db.Create(invalidFileName2);
+	TEST(err != KErrNone);
+
+	err = db.Open(invalidFileName1);
+	TEST(err != KErrNone);
+	err = db.Open(invalidFileName2);
+	TEST(err != KErrNone);
+
+	err = db.Create(KTestDbName1);
+	TEST2(err, KErrNone);
+	err = db.Exec(_L("CREATE TABLE A(I INTEGER)"));
+	TEST(err >= 0);
+	db.Close();
+	
+	RSqlSecurityPolicy sp = CreateSecurityPolicy();
+	err = db.Create(KTestDbName2, sp);
+	TEST2(err, KErrNone);
+	sp.Close();
+	err = db.Exec(_L("CREATE TABLE A(I INTEGER)"));
+	TEST(err >= 0);
+	db.Close();
+
+	TheTest.Printf(_L("Bad names test - public shared database\r\n"));
+	err = db.Open(KTestDbName1);	
+	TEST2(err, KErrNone);
+	DoBadNameTest(db);
+	db.Close();
+
+	TheTest.Printf(_L("Bad names test - public secure shared database\r\n"));
+	err = db.Open(KTestDbName2);	
+	TEST2(err, KErrNone);
+	DoBadNameTest(db);
+	db.Close();
+	
+	(void)RSqlDatabase::Delete(KTestDbName2);
+	(void)RSqlDatabase::Delete(KTestDbName1);
+	
+	err = RSqlDatabase::Copy(invalidFileName1, invalidFileName2);
+	TheTest.Printf(_L("Copy database, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+	err = RSqlDatabase::Delete(invalidFileName1);
+	TheTest.Printf(_L("Delete database-1, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+	err = RSqlDatabase::Delete(invalidFileName2);
+	TheTest.Printf(_L("Delete database-2, err=%d\r\n"), err);
+	TEST(err != KErrNone);
+	}
+	
+void DoTests()
+	{
+	TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1769 Bad client test "));
+	BadClientTest();
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4048 Bad names test"));
+	BadNameTest();
+	}
+
+TInt E32Main()
+	{
+	TheTest.Title();
+	
+	CTrapCleanup* tc = CTrapCleanup::New();
+	
+	__UHEAP_MARK;
+	
+	DeleteTestFiles();
+	CreateTestEnv();
+	DoTests();
+	DeleteTestFiles();
+	TheFs.Close();
+
+	__UHEAP_MARKEND;
+	
+	TheTest.End();
+	TheTest.Close();
+	
+	delete tc;
+
+	User::Heap().Check();
+	return KErrNone;
+	}