C26485和指针衰减与TCHAR异常处理程序



我不明白我收到的C26485警告。

我的代码是一个异常处理:

catch (CDBException* e)
{
TCHAR szError[_MAX_PATH];
e->GetErrorMessage(szError, _MAX_PATH);
AfxMessageBox(szError);
}

它在说:

ExpressionszError: No array to pointer decay (bounds.3).

GetErrorMessageAfxMessageBox都在标记这个

这对我来说没有意义。谢谢你的帮助。

这个诊断结果很不幸,因为代码分析的视角太窄,忽略了现成的提示。C26485警告数组到指针的衰减,这是c++最危险的特性之一。当将数组名传递给需要指针的函数时,编译器会将数组转换为指向其第一个元素的指针,从而删除数组类型中的大小信息。

调用接受单独参数(指针和大小)来描述数组的接口的客户端必须确保数组的大小与数组的大小实际匹配。这导致了无数的CVE,而且没有理由相信情况会好转。危险,有工具来防止它是很好的。在理论。

然而,这里的情况不同。GetErrorMessage的声明(和定义)具有SAL注释,允许编译器在编译时验证指针和大小是否匹配。签名如下:

virtual BOOL GetErrorMessage(_Out_writes_z_(nMaxError) LPTSTR lpszError,
_In_ UINT nMaxError,
_Out_opt_ PUINT pnHelpContext = NULL) const;

_Out_writes_z_(s)注释在指针lpszError和对应的数组大小nMaxError之间建立了一个编译时可验证的关系。这是有用的信息,应尽可能加以利用。

首先,让我们尝试解决当前的问题,遵循文档中的建议:

显式强制转换为衰变指针类型可以防止警告,但它不能防止错误代码.

将数组转换为指向其第一个元素的指针的最简洁的方法是:

catch (CDBException* e)
{
TCHAR szError[_MAX_PATH];
e->GetErrorMessage(&szError[0], _MAX_PATH);
AfxMessageBox(&szError[0]);
}

这修复了直接的问题(顺便说一下,在两个函数调用上,即使出于不同的原因)。不再发出C26485,并且作为额外的奖励,传递错误的值作为第二个参数(例如_MAX_PATH + 1)确实获得了所需的C6386诊断("缓冲区溢出">)。

作为验证正确性的一种方式,这也是至关重要的。如果您使用一种更间接的方式(比如,使用CString,就像这里建议的那样),您将立即放弃编译时验证。使用CString在计算上更昂贵,而且更不安全。

作为上述的替代方案,您还可以暂时抑制两个呼叫上的C26485诊断,例如

catch (CDBException* e)
{
TCHAR szError[_MAX_PATH];
// Decay is safe due to the _Out_writes_z_ annotation
#pragma warning(suppress : 26485)
e->GetErrorMessage(szError, _MAX_PATH);
// Decay is safe; the previous call guarantees zero-termination
#pragma warning(suppress : 26485)
AfxMessageBox(szError);
}

选择哪一个实现最终是个人偏好的问题。任何一种方法都可以解决这个问题,就代码分析而言,后者可能更保留一些。

关于为什么最后调用AfxMessageBox是安全的:它期望一个以零结尾的字符串,因此不需要显式的size参数。GetErrorMessage签名上的_Out_writes_z_(s)注释承诺在返回时始终以零结束输出字符串。这也是在编译时在合约的双方进行验证的:调用者可以依赖于接收一个以零结尾的字符串,编译器确保实现没有违反此后置条件的返回路径。

根据Andrew的建议,我应该在这里发布工作代码。

不使用TCHAR的解决方案

catch (CDBException* e)
{
e->ReportError();
e->Delete();
}

.

catch (CException* e)
{
CString strError;
e->GetErrorMessage(strError.GetBufferSetLength(_MAX_PATH), _MAX_PATH);
strError.ReleaseBuffer();
AfxMessageBox(strError);
e->Delete();
}

对于第二个示例,selon iinectable只静默代码分析。

要记住,通常你必须删除CException指针,你在这里忘记了吗?

最新更新