--- /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;
+ }