SAL annotation updates & fix warnings (#5617)

Various SAL annotations were either incorrect or not backed up by code. This PR address these issues.

NOTE: By default, Start-BuildNativeWindowsBinaries does not enable code analysis and issues detected by SAL annotations are not reported. To identify these issues, run Start-BuildNativeWindowsBinaries to generate the solution and vcxproj files. Launch a Visual Studio developer prompt, cd to src\powershell-native and run msbuild manually.

> msbuild PowerShellNative.sln /p:RunCodeAnalysis=true /p:Configuration=RelWithDebInfo /p:Platform=x64

The following changes address all code analysis warnings:

- GetRegistryInfo in NativeMsh had incorrect out annotations, remove __opt
- Fix handling of various out parameters - check for non-null and initialize
- Update and Align SAL annotations for GetFormattedErrorMessage overloads
- Allow PluginException to accept NULL message.
This commit is contained in:
Dan Travison 2018-01-05 16:53:27 -08:00 committed by Travis Plunk
parent 22f47295f4
commit da5d897823
8 changed files with 101 additions and 49 deletions

View file

@ -78,25 +78,27 @@ namespace NativeMsh
// Note: During successful calls the following values must be freed by the caller: // Note: During successful calls the following values must be freed by the caller:
// pwszMonadVersion // pwszMonadVersion
// pwszRuntimeVersion // pwszRuntimeVersion
// pwzsRegKeyValue // pwszRegKeyValue
// //
// The caller must take care to check to see if they must be freed during error scenarios // The caller must take care to check to see if they must be freed during error scenarios
// because the function may fail after allocating one or more strings. // because the function may fail after allocating one or more strings.
// //
_Success_(return == 0)
unsigned int GetRegistryInfo( unsigned int GetRegistryInfo(
__deref_out_opt PWSTR * pwszMonadVersion, __out PWSTR * pwszMonadVersion,
__inout_ecount(1) int * lpMonadMajorVersion, __inout_ecount(1) int * lpMonadMajorVersion,
int monadMinorVersion, int monadMinorVersion,
__deref_out_opt PWSTR * pwszRuntimeVersion, __out PWSTR * pwszRuntimeVersion,
LPCWSTR lpszRegKeyNameToRead, LPCWSTR lpszRegKeyNameToRead,
__deref_out_opt PWSTR * pwzsRegKeyValue); __out PWSTR * pwszRegKeyValue);
_Success_(return == 0)
unsigned int GetRegistryInfo( unsigned int GetRegistryInfo(
__deref_out_opt PWSTR * pwszMonadVersion, __out PWSTR * pwszMonadVersion,
__inout_ecount(1) int * lpMonadMajorVersion, __inout_ecount(1) int * lpMonadMajorVersion,
int monadMinorVersion, int monadMinorVersion,
__deref_out_opt PWSTR * pwszRuntimeVersion, __out PWSTR * pwszRuntimeVersion,
__deref_out_opt PWSTR * pwszConsoleHostAssemblyName); __out PWSTR * pwszConsoleHostAssemblyName);
unsigned int LaunchCoreCLR( unsigned int LaunchCoreCLR(
ClrHostWrapper* hostWrapper, ClrHostWrapper* hostWrapper,

View file

@ -1238,18 +1238,19 @@ namespace NativeMsh
// Note: During successful calls the following values must be freed by the caller: // Note: During successful calls the following values must be freed by the caller:
// pwszMonadVersion // pwszMonadVersion
// pwszRuntimeVersion // pwszRuntimeVersion
// pwzsRegKeyValue // pwszRegKeyValue
// //
// The caller must take care to check to see if they must be freed during error scenarios // The caller must take care to check to see if they must be freed during error scenarios
// because the function may fail after allocating one or more strings. // because the function may fail after allocating one or more strings.
// //
_Success_(return == 0)
unsigned int PwrshCommon::GetRegistryInfo( unsigned int PwrshCommon::GetRegistryInfo(
__deref_out_opt PWSTR * pwszMonadVersion, __out PWSTR * pwszMonadVersion,
__inout_ecount(1) int * lpMonadMajorVersion, __inout_ecount(1) int * lpMonadMajorVersion,
int monadMinorVersion, int monadMinorVersion,
__deref_out_opt PWSTR * pwszRuntimeVersion, __out PWSTR * pwszRuntimeVersion,
LPCWSTR lpszRegKeyNameToRead, LPCWSTR lpszRegKeyNameToRead,
__deref_out_opt PWSTR * pwzsRegKeyValue) __out PWSTR * pwszRegKeyValue)
{ {
HKEY hEngineKey = NULL; HKEY hEngineKey = NULL;
bool bEngineKeyOpened = true; bool bEngineKeyOpened = true;
@ -1257,12 +1258,27 @@ namespace NativeMsh
wchar_t * wszMshEngineRegKeyPath = NULL; wchar_t * wszMshEngineRegKeyPath = NULL;
LPWSTR wszFullMonadVersion = NULL; LPWSTR wszFullMonadVersion = NULL;
if (NULL != pwszRegKeyValue)
{
*pwszRegKeyValue = NULL;
}
if (NULL != pwszRuntimeVersion)
{
*pwszRuntimeVersion = NULL;
}
if (NULL != pwszMonadVersion)
{
*pwszMonadVersion = NULL;
}
do do
{ {
if (NULL == pwszMonadVersion || if (NULL == pwszMonadVersion ||
NULL == lpMonadMajorVersion || NULL == lpMonadMajorVersion ||
NULL == pwszRuntimeVersion || NULL == pwszRuntimeVersion ||
NULL == pwzsRegKeyValue) NULL == pwszRegKeyValue)
{ {
exitCode = EXIT_CODE_READ_REGISTRY_FAILURE; exitCode = EXIT_CODE_READ_REGISTRY_FAILURE;
break; break;
@ -1315,7 +1331,7 @@ namespace NativeMsh
{ {
LPCWSTR wszRequestedRegValueName = lpszRegKeyNameToRead; LPCWSTR wszRequestedRegValueName = lpszRegKeyNameToRead;
if (!this->RegQueryREG_SZValue(hEngineKey, wszRequestedRegValueName, if (!this->RegQueryREG_SZValue(hEngineKey, wszRequestedRegValueName,
wszMshEngineRegKeyPath, pwzsRegKeyValue)) wszMshEngineRegKeyPath, pwszRegKeyValue))
{ {
exitCode = EXIT_CODE_READ_REGISTRY_FAILURE; exitCode = EXIT_CODE_READ_REGISTRY_FAILURE;
break; break;
@ -1361,12 +1377,13 @@ namespace NativeMsh
return exitCode; return exitCode;
} }
_Success_(return == 0)
unsigned int PwrshCommon::GetRegistryInfo( unsigned int PwrshCommon::GetRegistryInfo(
__deref_out_opt PWSTR * pwszMonadVersion, __out PWSTR * pwszMonadVersion,
__inout_ecount(1) int * lpMonadMajorVersion, __inout_ecount(1) int * lpMonadMajorVersion,
int monadMinorVersion, int monadMinorVersion,
__deref_out_opt PWSTR * pwszRuntimeVersion, __out PWSTR * pwszRuntimeVersion,
__deref_out_opt PWSTR * pwszConsoleHostAssemblyName) __out PWSTR * pwszConsoleHostAssemblyName)
{ {
return GetRegistryInfo(pwszMonadVersion, return GetRegistryInfo(pwszMonadVersion,
lpMonadMajorVersion, lpMonadMajorVersion,

View file

@ -217,7 +217,7 @@ int __cdecl wmain(const int argc, const wchar_t* argv[])
if (debug) if (debug)
{ {
::wprintf(L" Attach the debugger to powershell.exe and press any key to continue\n"); ::wprintf(L" Attach the debugger to powershell.exe and press any key to continue\n");
::getchar(); (void) ::getchar();
} }
if (helpRequested) if (helpRequested)

View file

@ -31,15 +31,18 @@ LPCWSTR g_MAIN_BINARY_NAME = L"pwrshplugin.dll";
// the caller should free pwszErrorMessage using LocalFree(). // the caller should free pwszErrorMessage using LocalFree().
// returns: If the function succeeds the return value is the number of CHARs stored int the output // returns: If the function succeeds the return value is the number of CHARs stored int the output
// buffer, excluding the terminating null character. If the function fails the return value is zero. // buffer, excluding the terminating null character. If the function fails the return value is zero.
#pragma prefast(push) _Success_(return > 0)
#pragma prefast (disable: 28196) DWORD GetFormattedErrorMessage(__out PWSTR * pwszErrorMessage, DWORD dwMessageId, va_list* arguments)
DWORD GetFormattedErrorMessage(__deref_out PWSTR * pwszErrorMessage, DWORD dwMessageId, va_list* arguments)
#pragma prefast(pop)
{ {
DWORD dwLength = 0; DWORD dwLength = 0;
LPWSTR wszSystemErrorMessage = NULL;
do do
{ {
if (NULL == pwszErrorMessage)
{
break;
}
*pwszErrorMessage = NULL; *pwszErrorMessage = NULL;
if (NULL == g_hResourceInstance) if (NULL == g_hResourceInstance)
@ -51,8 +54,6 @@ DWORD GetFormattedErrorMessage(__deref_out PWSTR * pwszErrorMessage, DWORD dwMes
#endif #endif
} }
LPWSTR wszSystemErrorMessage = NULL;
//string function
dwLength = FormatMessageW( dwLength = FormatMessageW(
FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER, FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER,
g_hResourceInstance, g_hResourceInstance,
@ -62,28 +63,36 @@ DWORD GetFormattedErrorMessage(__deref_out PWSTR * pwszErrorMessage, DWORD dwMes
0, 0,
arguments); arguments);
if (dwLength > 0) if (dwLength == 0)
{ {
break;
}
*pwszErrorMessage = new WCHAR[dwLength + 1]; *pwszErrorMessage = new WCHAR[dwLength + 1];
if (NULL != *pwszErrorMessage) if (*pwszErrorMessage == NULL)
{ {
//string function dwLength = 0;
break;
}
if (FAILED(StringCchCopyW(*pwszErrorMessage, dwLength + 1, wszSystemErrorMessage))) if (FAILED(StringCchCopyW(*pwszErrorMessage, dwLength + 1, wszSystemErrorMessage)))
{ {
dwLength = 0; dwLength = 0;
delete [] (*pwszErrorMessage); delete [] (*pwszErrorMessage);
*pwszErrorMessage = NULL; *pwszErrorMessage = NULL;
} }
}
LocalFree(wszSystemErrorMessage);
}
}while(false); }while(false);
if (NULL != wszSystemErrorMessage)
{
LocalFree(wszSystemErrorMessage);
}
return dwLength; return dwLength;
} }
DWORD GetFormattedErrorMessage(__deref_out PZPWSTR pwszErrorMessage, DWORD dwMessageId, ...) DWORD GetFormattedErrorMessage(__out PWSTR * pwszErrorMessage, DWORD dwMessageId, ...)
{ {
DWORD result = 0; DWORD result = 0;
@ -307,12 +316,19 @@ DWORD ReportOperationComplete(WSMAN_PLUGIN_REQUEST *requestDetails, DWORD errorC
DWORD result = EXIT_CODE_SUCCESS; DWORD result = EXIT_CODE_SUCCESS;
PWSTR pwszErrorMessage = NULL; PWSTR pwszErrorMessage = NULL;
GetFormattedErrorMessage(&pwszErrorMessage, errorCode); if (GetFormattedErrorMessage(&pwszErrorMessage, errorCode) == 0)
result = WSManPluginOperationComplete(requestDetails, 0, errorCode, pwszErrorMessage);
if (NULL != pwszErrorMessage)
{ {
/* if an error message could not be loaded, generate a fallback
message to provide a level of diagnosability.
*/
WCHAR szFallbackMessageMessage[64] = L"\0";
swprintf_s(szFallbackMessageMessage, _countof(szFallbackMessageMessage), L"ReportOperationComplete: %d", errorCode);
result = WSManPluginOperationComplete(requestDetails, 0, errorCode, szFallbackMessageMessage);
}
else
{
result = WSManPluginOperationComplete(requestDetails, 0, errorCode, pwszErrorMessage);
delete[] pwszErrorMessage; delete[] pwszErrorMessage;
} }

View file

@ -13,6 +13,6 @@
const int EXIT_CODE_BAD_INPUT = 100; const int EXIT_CODE_BAD_INPUT = 100;
extern DWORD GetFormattedErrorMessage(__deref_out PWSTR * pwszErrorMessage, DWORD dwMessageId, ...); extern DWORD GetFormattedErrorMessage(__out PWSTR * pwszErrorMessage, DWORD dwMessageId, ...);
extern unsigned int ConstructPowerShellVersion(int iPSMajorVersion, int iPSMinorVersion, __deref_out_opt PWSTR *pwszMonadVersion); extern unsigned int ConstructPowerShellVersion(int iPSMajorVersion, int iPSMinorVersion, __deref_out_opt PWSTR *pwszMonadVersion);

View file

@ -270,7 +270,10 @@ unsigned int PowerShellClrWorker::LoadWorkerCallbackPtrs(
{ {
PWSTR msg = NULL; PWSTR msg = NULL;
exitCode = g_MANAGED_METHOD_RESOLUTION_FAILED; exitCode = g_MANAGED_METHOD_RESOLUTION_FAILED;
GetFormattedErrorMessage(&msg, exitCode); /* NOTE: don't use a string literal for a fallback; PlugInException expects msg to allocated
and will free it but also supports NULL.
*/
(void) GetFormattedErrorMessage(&msg, exitCode);
*pPluginException = new PlugInException(exitCode, msg); *pPluginException = new PlugInException(exitCode, msg);
return exitCode; return exitCode;
} }

View file

@ -490,6 +490,11 @@ private:
{ {
iMajorVersion = iVPSMajorVersion; iMajorVersion = iVPSMajorVersion;
if (NULL != wszMgdPlugInFileName)
{
*wszMgdPlugInFileName = NULL;
}
exitCode = ConstructPowerShellVersion(iVPSMajorVersion, iVPSMinorVersion, &wszMonadVersion); exitCode = ConstructPowerShellVersion(iVPSMajorVersion, iVPSMinorVersion, &wszMonadVersion);
if (EXIT_CODE_SUCCESS != exitCode) if (EXIT_CODE_SUCCESS != exitCode)
{ {
@ -561,10 +566,19 @@ private:
_In_ PWSTR wszMgdPlugInFileName, _In_ PWSTR wszMgdPlugInFileName,
_In_ PWSTR wszVCLRVersion, // Conditionally set to wszCLRVersion on success _In_ PWSTR wszVCLRVersion, // Conditionally set to wszCLRVersion on success
_In_ PWSTR wszVAppBase, // Conditionally set to wszAppBase on success _In_ PWSTR wszVAppBase, // Conditionally set to wszAppBase on success
__out_opt PlugInException** pPluginException ) __out PlugInException** pPluginException )
{ {
unsigned int exitCode = EXIT_CODE_SUCCESS; unsigned int exitCode = EXIT_CODE_SUCCESS;
if (NULL != pPluginException)
{
*pPluginException = NULL;
}
else
{
return g_INVALID_INPUT;
}
if (bIsPluginLoaded) if (bIsPluginLoaded)
{ {
return g_MANAGED_PLUGIN_ALREADY_LOADED; return g_MANAGED_PLUGIN_ALREADY_LOADED;
@ -577,8 +591,6 @@ private:
do do
{ {
*pPluginException = NULL;
// Setting global AppBase and CLR Version // Setting global AppBase and CLR Version
wszCLRVersion = wszVCLRVersion; wszCLRVersion = wszVCLRVersion;
wszAppBase = wszVAppBase; wszAppBase = wszVAppBase;
@ -768,7 +780,9 @@ private:
else else
{ {
PWSTR msg = NULL; PWSTR msg = NULL;
GetFormattedErrorMessage(&msg, g_OPTION_SET_NOT_COMPLY, g_BUILD_VERSION); (void) GetFormattedErrorMessage(&msg, g_OPTION_SET_NOT_COMPLY, g_BUILD_VERSION);
/* NOTE: Passing possible NULL msg. PlugInExpecption requires allocated
or NULL. Literal strings are not supported. */
throw new PlugInException(exitCode, msg); throw new PlugInException(exitCode, msg);
} }
} }

View file

@ -140,7 +140,7 @@ public:
// the memory. // the memory.
PWSTR extendedErrorInformation; PWSTR extendedErrorInformation;
PlugInException(DWORD msgId, __in PWSTR msg) PlugInException(DWORD msgId, __in_opt PWSTR msg)
{ {
dwMessageId = msgId; dwMessageId = msgId;
extendedErrorInformation = msg; extendedErrorInformation = msg;