#include "stdafx.h" #include "sxsplugMap.h" #define PRAGMA_UNSAFE_DELIMITER_DEFAULT ' ' #define PRAGMA_UNSAFE_DELIMITER_BETWEEN_STATEMENT ';' #define PRAGMA_UNSAFE_DELIMITER_BETWEEN_VALUESTR ',' #define PRAGMA_UNSAFE_DELIMITER_BETWEEN_KEYWORD_AND_VALUESTR ':' #define PRAGMA_UNSAFE_KEYWORD_UNSAFE "unsafe" #define PRAGMA_UNSAFE_KEYWORD_UNSAFE_PUSH "push" #define PRAGMA_UNSAFE_KEYWORD_UNSAFE_DISABLE "disable" #define PRAGMA_UNSAFE_KEYWORD_UNSAFE_ENABLE "enable" #define PRAGMA_UNSAFE_KEYWORD_UNSAFE_POP "pop" #define PRAGMA_UNSAFE_GETUSAFEOPERPARAMETERS_DWFLAG_UNSAFE_ENABLE 0 #define PRAGMA_UNSAFE_GETUSAFEOPERPARAMETERS_DWFLAG_UNSAFE_DISABLE 1 BOOL CPragmaUnsafe_UnsafeFunctionStateStack::ReInitialize() { m_UnsafeFuncs.clear(); // void function m_fInitialized = FALSE; return this->Initialize(); } BOOL CPragmaUnsafe_UnsafeFunctionStateStack::Initialize() { ASSERT(m_fInitialized == FALSE); m_index = 0; BOOL fSuccess = AddFunctionIntoStack(POINTER_ARITHMATIC_FUNC); if (fSuccess) m_fInitialized = TRUE; return fSuccess; } BOOL CPragmaUnsafe_UnsafeFunctionStateStack::IsFunctionNotUnsafe(const char * strFuncName) { BOOL fSafe = TRUE; // defaultly all function are SAFE PragmaUnsafe_PRAGMA_UNSAFE_FUNCTIONS::iterator pIter; if (true == m_UnsafeFuncs.empty()) return TRUE; DWORD CurrentIndex = m_index - 1; for (pIter = m_UnsafeFuncs.begin(); pIter != m_UnsafeFuncs.end(); pIter ++) { if (pIter->first.compare(strFuncName) == 0) { PragmaUnsafe_FUNCTION_STATUS & FuncStatusRecord = pIter->second; // // get current status : enabled or not // BYTE x = (FuncStatusRecord[CurrentIndex / sizeof(BYTE)] & (1 << (CurrentIndex % sizeof(BYTE)))) >> (CurrentIndex % sizeof(BYTE)); // duplicate last status if (x == 0){ fSafe = FALSE; } break; // find a result already } } return fSafe; } BOOL CPragmaUnsafe_UnsafeFunctionStateStack::OnUnsafeDisable(const char * strFuncNameGroups) { if (FALSE == IsInitialized()) { PragmaUnsafe_ReportError("Not Initialized !!!\n"); return FALSE; } if (IsStackFull()) { PragmaUnsafe_ReportError("Stack is Full Sized now!\n"); return FALSE; } return ResetStack(strFuncNameGroups, false); } VOID CPragmaUnsafe_UnsafeFunctionStateStack::PackStack() { if ( m_index == 0) return; BYTE AllEnabledStatus[8]; for ( DWORD i = 0; i < (m_index - 1) / sizeof(BYTE); i++) AllEnabledStatus[i] = 0xFF; AllEnabledStatus[(m_index - 1) / sizeof(BYTE)] = ((1 << (((m_index - 1)% sizeof(BYTE)) + 1)) - 1) & 0xFF; // // if from 0..m_index - 1, all state is enabled: just delete this function from the map // for (PragmaUnsafe_PRAGMA_UNSAFE_FUNCTIONS::iterator pIter = m_UnsafeFuncs.begin(); pIter != m_UnsafeFuncs.end(); pIter ++) { if (memcmp((PVOID)(&pIter->second[0]), AllEnabledStatus, PragmaUnsafe_STACK_SIZE_IN_BYTE) == 0) m_UnsafeFuncs.erase(pIter->first); } // // no func in the stack, clean the map and reset m_index == 0; // if (m_UnsafeFuncs.empty()) { m_UnsafeFuncs.clear(); m_index = 0; } } BOOL CPragmaUnsafe_UnsafeFunctionStateStack::OnUnsafePop() { if (FALSE == IsInitialized()) { PragmaUnsafe_ReportError("Not Initialized !!!\n"); return FALSE; } ASSERT(m_index > 0); if (IsStackEmpty()) { PragmaUnsafe_ReportError("Stack is current empty!\n"); return FALSE; } m_index--; if (m_index == 0) { m_UnsafeFuncs.clear(); // delete the map } PackStack(); // void function return TRUE; } BOOL CPragmaUnsafe_UnsafeFunctionStateStack::OnUnsafePush() { BOOL fSuccess = FALSE; PragmaUnsafe_PRAGMA_UNSAFE_FUNCTIONS::iterator pIter, qIter; string strFuncName; DWORD CurrentIndex; if (FALSE == IsInitialized()) { PragmaUnsafe_ReportError("Not Initialized !!!\n"); goto Exit; } if (IsStackFull()) { PragmaUnsafe_ReportError("Stack is Full Sized now!\n"); goto Exit; } CurrentIndex = (m_index - 1); ASSERT(CurrentIndex >= 0); //because we have check that the stack is not empty for (pIter = m_UnsafeFuncs.begin(); pIter != m_UnsafeFuncs.end(); pIter ++) { PragmaUnsafe_FUNCTION_STATUS & FuncStatusRecord = pIter->second; // ref is return... // // get current status of each function // BYTE x = (FuncStatusRecord[CurrentIndex / sizeof(BYTE)] & (1 << (CurrentIndex % sizeof(BYTE)))) >> (CurrentIndex % sizeof(BYTE)); ASSERT((x == 0) || (x == 1)); // duplicate last status if ( x == 1) FuncStatusRecord[m_index / sizeof(BYTE)] |= ((1 << (m_index % sizeof(BYTE))) & 0x00ff); else FuncStatusRecord[m_index / sizeof(BYTE)] &= (~((1 << (m_index % sizeof(BYTE))) & 0x00ff) & 0x00ff); } m_index ++; fSuccess = TRUE; Exit: return fSuccess; } BOOL CPragmaUnsafe_UnsafeFunctionStateStack::OnUnsafeEnable(const char * strFuncNameGroups) { if (FALSE == IsInitialized()) { PragmaUnsafe_ReportError("Not Initialized !!!\n"); return FALSE; } if (IsStackEmpty()) { PragmaUnsafe_ReportError("Stack is Empty now!\n"); return TRUE; } return ResetStack(strFuncNameGroups, true); } // if a function is already in the stack, change current status // if a function is not in the stack: // if you try to disable it : add it to the stack and would disfunction after pop is done // if you try to enable it : we cannot igore it in case that it go with a push and later a pop, so // just add it to the stack and would disfunction after pop is done BOOL CPragmaUnsafe_UnsafeFunctionStateStack::ResetStack(const char * strFuncNameGroups, bool fEnable) { BOOL fSuccess = FALSE; PragmaUnsafe_PRAGMA_UNSAFE_FUNCTIONS::iterator pIter, qIter; string strFuncName; istrstream streamFuncNameStream(strFuncNameGroups); DWORD CurIndex; // // suppose that the func names are delimited using ; // for each function which reset status // for (; getline(streamFuncNameStream, strFuncName, PRAGMA_UNSAFE_DELIMITER_BETWEEN_VALUESTR); ) { if (strFuncName.empty()) break; qIter = m_UnsafeFuncs.find(strFuncName); // // this function is not on map currently, // if (qIter == m_UnsafeFuncs.end()) { // // adding into the stack as a disabled function, see the comments at the function declaration // if ( FALSE == AddFunctionIntoStack(strFuncName.c_str(), fEnable)) { PragmaUnsafe_ReportError("AddFunctionIntoStack for %s failed\n", strFuncName.c_str()); } continue; } ASSERT(m_index > 0); CurIndex = m_index - 1; PragmaUnsafe_FUNCTION_STATUS & FuncStatusRecord = qIter->second; // overwrite the current status if (fEnable == true) FuncStatusRecord[CurIndex / sizeof(BYTE)] |= ((1 << (CurIndex % sizeof(BYTE))) & 0xff); else FuncStatusRecord[CurIndex / sizeof(BYTE)] &= (~((1 << (CurIndex % sizeof(BYTE))) & 0xff) & 0xff); } fSuccess = TRUE; return fSuccess; } void TrimString(string & strFuncName, DWORD dwFlag = STRING_TRIM_FLAG_LEFT | STRING_TRIM_FLAG_RIGHT) { int i; if (dwFlag & STRING_TRIM_FLAG_LEFT) { // left trim i = 0; while ((strFuncName[i] == ' ') && (i < strFuncName.length())) i++; if ( i > 0) strFuncName.erase(0,i); } if (dwFlag & STRING_TRIM_FLAG_RIGHT) { // right trim i = strFuncName.length() - 1; while ((strFuncName[i] == ' ') && (i >= 0 )) i--; if ( i != strFuncName.length() - 1) strFuncName.erase(i, (strFuncName.length() - i)); } return; } // // when this function is called, this func must not in the current stack // BOOL CPragmaUnsafe_UnsafeFunctionStateStack::AddFunctionIntoStack(const char * strFuncNameGroups, bool fEnabled) { BOOL fSuccess = FALSE; string strFuncName; istrstream streamFuncNameStream(strFuncNameGroups); PragmaUnsafe_FUNCTION_STATUS new_func_status; DWORD CurrentIndex; if (m_index == 0) m_index ++; // // suppose that the func names are delimited using ; // CurrentIndex = m_index -1 ; for (; getline(streamFuncNameStream, strFuncName, PRAGMA_UNSAFE_DELIMITER_BETWEEN_VALUESTR); ) { if (strFuncName.empty()) break; TrimString(strFuncName); // left-trim and right-trim if (strFuncName.empty()) break; if (m_UnsafeFuncs.find(strFuncName) != m_UnsafeFuncs.end()) { // // If the function has already in the map, we just ignore it. // This would deal with a header file with "#pragam unsafe(disable: func1)" is included multiple times. // that is, if the sequence is // #pragam unsafe(disable: func1) // #pragam unsafe(push, enable:func1) // #pragam unsafe(disable:func1) ---> would be ignored, and func1 is still enabled at this moment // #pragam unsafe(pop) // // in this case, a warning message would be issued //PragmaUnsafe_ReportWarning(PragmaUnsafe_PLUGIN_WARNING_MSG_PREFIX, "%s has already been disabled\n", strFuncName); PragmaUnsafe_ReportError("%s has already been disabled\n", strFuncName.c_str()); continue; } ZeroMemory(&new_func_status, sizeof(new_func_status)); // grow to the same size as all other functions // set to be "1" for the range of 0..CurrentIndex-1 if (CurrentIndex > sizeof(BYTE) + 1) { for (int i = 0 ; i < ((CurrentIndex - 1) / sizeof(BYTE)); i++) new_func_status[i] = 0xFF; } if (fEnabled == true) { new_func_status[CurrentIndex / sizeof(BYTE)] = ((1 << ((CurrentIndex % sizeof(BYTE)) + 1)) - 1) & 0xFF; } else { new_func_status[CurrentIndex / sizeof(BYTE)] = ((1 << (CurrentIndex % sizeof(BYTE))) - 1) & 0xFF; } m_UnsafeFuncs.insert(PragmaUnsafe_PRAGMA_UNSAFE_FUNCTIONS::value_type(strFuncName, new_func_status)); } fSuccess = TRUE; //Exit: return fSuccess; } VOID CPragmaUnsafe_UnsafeFunctionStateStack::PrintFunctionCurrentStatus(int level) { PragmaUnsafe_PRAGMA_UNSAFE_FUNCTIONS::iterator pIter, qIter; cout << endl << endl << "CurrentStack:" << endl; cout << "m_index = " << m_index << endl; // // for each current item in map, push to preserve its current status // if (m_index == 0) { return; } DWORD CurrentIndex = (m_index - 1); BYTE x; for (pIter = m_UnsafeFuncs.begin(); pIter != m_UnsafeFuncs.end(); pIter ++) { PragmaUnsafe_FUNCTION_STATUS & FuncStatusRecord = pIter->second; // ref is return... // // get current status of each function // x = (FuncStatusRecord[CurrentIndex / sizeof(BYTE)] & (1 << (CurrentIndex % sizeof(BYTE)))) >> (CurrentIndex % sizeof(BYTE)); for ( int j = 0 ; j < level; j++) cout << " "; cout << pIter->first << ":"<< ((x == 0) ? "Disabled" : "Enabled") << endl; } return; } // // this function is only called when the end of file is reached // BOOL CPragmaUnsafe_UnsafeFunctionStateStack::CheckIntegrityAtEndOfFile() { if (m_index == 1) // should always be 1 since pointer_arithmatic is default return TRUE; else return FALSE; } /* at each file beginning: reset the pragma stack because of its file-range */ BOOL PragmaUnsafe_OnFileStart() { // // initalize the map structure everytime when prefast start to parse // return Sxs_PragmaUnsafedFunctions.ReInitialize(); } /* at each file end: verify integrity of the stake */ BOOL PragmaUnsafe_OnFileEnd() { //Sxs_PragmaUnsafedFunctions.PrintFunctionCurrentStatus(0); return Sxs_PragmaUnsafedFunctions.CheckIntegrityAtEndOfFile(); } VOID PragmaUnsafe_GetUsafeOperParameters(DWORD dwFlag, const string & strPragmaUnsafeSingleStatement, string & strFuncNameList) { // initialize strFuncNameList.erase(); int iPrefix = 0; if ( dwFlag == PRAGMA_UNSAFE_GETUSAFEOPERPARAMETERS_DWFLAG_UNSAFE_ENABLE) iPrefix = strlen(PRAGMA_UNSAFE_KEYWORD_UNSAFE_ENABLE); else if ( dwFlag == PRAGMA_UNSAFE_GETUSAFEOPERPARAMETERS_DWFLAG_UNSAFE_DISABLE) iPrefix = strlen(PRAGMA_UNSAFE_KEYWORD_UNSAFE_DISABLE); if (iPrefix == 0) // error case { goto ErrorExit; } strFuncNameList.assign(strPragmaUnsafeSingleStatement); strFuncNameList.erase(0, iPrefix); TrimString(strFuncNameList); // should be in the format of [enable|disbale]: func1, func2, func3 if (strFuncNameList[0] != PRAGMA_UNSAFE_DELIMITER_BETWEEN_KEYWORD_AND_VALUESTR) { goto ErrorExit; } strFuncNameList.erase(0, 1); // get rid : TrimString(strFuncNameList); goto Exit; ErrorExit: if (!strFuncNameList.empty()) strFuncNameList.erase(); Exit: return; } BOOL PragmaUnsafe_OnPragma(char * str, PRAGMA_STATEMENT & ePragmaUnsafe) { BOOL fSuccess = FALSE; istrstream streamParagmaString(str); string strPragmaUnsafeSingleStatement; string strFuncNameList; ePragmaUnsafe = PRAGMA_NOT_UNSAFE_STATEMENT; // // check whether it begins with "unsafe", that is, its prefix is "unsafe:" // get the first string which is sperate from the left using ' ' // getline(streamParagmaString, strPragmaUnsafeSingleStatement, ':'); TrimString(strPragmaUnsafeSingleStatement); // void func if (true == strPragmaUnsafeSingleStatement.empty()) { ePragmaUnsafe = PRAGMA_NOT_UNSAFE_STATEMENT; fSuccess = TRUE; goto Exit; } // // pragam unsafe keyword comparsion is case-sensitive // if (strncmp(strPragmaUnsafeSingleStatement.c_str(), PRAGMA_UNSAFE_KEYWORD_UNSAFE, strlen(PRAGMA_UNSAFE_KEYWORD_UNSAFE)) != 0) { // not start with Keyword "unsafe" ePragmaUnsafe = PRAGMA_NOT_UNSAFE_STATEMENT; fSuccess = TRUE; goto Exit; } // so far, the statement is valid ePragmaUnsafe = PRAGMA_UNSAFE_STATEMENT_VALID; for (; getline(streamParagmaString, strPragmaUnsafeSingleStatement, PRAGMA_UNSAFE_DELIMITER_BETWEEN_STATEMENT); ) { // // to get a statement begin with "push", or "enable", or "disable", or "pop", // we deal with push/pop first because they are non-parameter statements // TrimString(strPragmaUnsafeSingleStatement); if (strPragmaUnsafeSingleStatement.compare(PRAGMA_UNSAFE_KEYWORD_UNSAFE_PUSH) == 0) { Sxs_PragmaUnsafedFunctions.OnUnsafePush(); } else if (strPragmaUnsafeSingleStatement.compare(PRAGMA_UNSAFE_KEYWORD_UNSAFE_POP) == 0) { Sxs_PragmaUnsafedFunctions.OnUnsafePop(); } else if (strncmp(strPragmaUnsafeSingleStatement.c_str(), PRAGMA_UNSAFE_KEYWORD_UNSAFE_ENABLE, strlen(PRAGMA_UNSAFE_KEYWORD_UNSAFE_ENABLE)) == 0) { PragmaUnsafe_GetUsafeOperParameters(PRAGMA_UNSAFE_GETUSAFEOPERPARAMETERS_DWFLAG_UNSAFE_ENABLE, strPragmaUnsafeSingleStatement, strFuncNameList); if (strFuncNameList.empty()) { PragmaUnsafe_ReportError("Invalid string for pragma unsafe: %s\n", strPragmaUnsafeSingleStatement.c_str()); goto Exit; } else { Sxs_PragmaUnsafedFunctions.OnUnsafeEnable(strFuncNameList.c_str()); } } else if (strncmp(strPragmaUnsafeSingleStatement.c_str(), PRAGMA_UNSAFE_KEYWORD_UNSAFE_DISABLE, strlen(PRAGMA_UNSAFE_KEYWORD_UNSAFE_DISABLE)) == 0) { PragmaUnsafe_GetUsafeOperParameters(PRAGMA_UNSAFE_GETUSAFEOPERPARAMETERS_DWFLAG_UNSAFE_DISABLE, strPragmaUnsafeSingleStatement, strFuncNameList); if (strFuncNameList.empty()) { PragmaUnsafe_ReportError("Invalid string for pragma unsafe: %s\n", strPragmaUnsafeSingleStatement.c_str()); goto Exit; } else { Sxs_PragmaUnsafedFunctions.OnUnsafeDisable(strFuncNameList.c_str()); } } else { // invalid string in pragma beginning with "unsafe" ePragmaUnsafe = PRAGMA_UNSAFE_STATEMENT_INVALID; PragmaUnsafe_ReportError("Invalid string for pragma unsafe: %s\n", strPragmaUnsafeSingleStatement.c_str()); goto Exit; } } //Sxs_PragmaUnsafedFunctions.PrintFunctionCurrentStatus(0); fSuccess = TRUE; Exit: return fSuccess; } BOOL PragmaUnsafe_IsPointerArithmaticEnabled() { return Sxs_PragmaUnsafedFunctions.IsFunctionNotUnsafe(POINTER_ARITHMATIC_FUNC); } int ReportInternalError(int nLine) { _tprintf(TEXT("%hs(%d) : Internal Error Occurred\n"), __FILE__, nLine); return 0; }