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

C26485 and pointer decay with TCHAR in exception handler

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

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

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

它在说:

Expression szError: No array to pointer decay (bounds.3).

GetErrorMessageAfxMessageBox 都在标记这个。

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

根据 Andrew 的建议,我应该 post 此处的工作代码。

不使用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 IInsepctable 仅静音代码分析。

并且要记住,通常您必须删除您在这里忘记的 CException 指针?

此诊断是代码分析的一个不幸结果,其观点过于狭隘,忽略了现成的提示。 C26485 警告数组到指针的衰减,这是 C++ 最危险的特性之一。将数组名称传递给需要指针的函数时,编译器会默默地将数组转换为指向其第一个元素的指针,从而删除作为数组类型一部分的大小信息。

调用接受单个参数(指针和大小)来描述数组的接口的客户端必须确保大小与数组实际匹配。这已经引起了无数 CVE 的关注,没有理由相信情况会有所好转。数组到指针的衰减 危险的,有工具来防范它是很好的。理论上。

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

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

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

不过,首先,让我们按照 documentation 的建议尝试解决眼前的问题:

An explicit cast to the decayed pointer type prevents the warning, but it doesn't prevent buggy code.

将数组转换为指向其第一个元素的指针的最简洁方法就是照字面意思去做:

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 是安全的:它需要一个以零结尾的字符串,因此不需要显式的大小参数。 GetErrorMessage 签名上的 _Out_writes_z_(s) 注释承诺始终以零终止 return 上的输出字符串。这也在编译时在契约的双方进行了验证:调用者可以依赖于接收以零终止的字符串,并且编译器确保 实现 没有 return 违反此 post 条件的路径。