为什么这段代码用MSVS2012触发“写溢出警告(C6386)”

问题描述 投票:0回答:2

我有以下一段C代码:

#include <stdint.h>

typedef union{
    uint8_t c[4];
    uint16_t s[2];
    uint32_t l;
}U4;

uint32_t cborder32(uint32_t l)
{
    U4 mask,res;
    unsigned char* p = (unsigned char*)&l;
    mask.l = 0x00010203;
    res.c[(uint8_t)(mask.c[0])] = (uint8_t)p[0]; // <-- this line gives C6386
    res.c[(uint8_t)(mask.c[1])] = (uint8_t)p[1];
    res.c[(uint8_t)(mask.c[2])] = (uint8_t)p[2];
    res.c[(uint8_t)(mask.c[3])] = (uint8_t)p[3];
    return res.l;
}

并且在对其运行代码分析时会触发写入溢出警告。 http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=EN-US&k=k%28C6386%29&rd=true

错误是:

C6386 Write overrun Buffer overrun while writing to 'res.c': writable size is '4' bytes, but '66052' bytes might be written. 对 'res.c[66051]' 的写入无效,(可写范围为 0 到 3)

我只是不明白为什么......有没有人可以解释我为什么?

c
2个回答
1
投票

我将其记为 Microsoft 产品中的潜在 错误。在计算数组索引时,它似乎使用了

mask.l
的完整值(
0x01020304
是十进制的
66051
),尽管事实上您显然希望
mask.c[0]
被强制为
uint8_t
值。

所以第一步是通知微软。他们 may 回来告诉你你错了,并希望给你 C++ 标准部分,说明 为什么 你做的是错的。或者他们可能只是说代码分析工具是“尽力而为”。因为它实际上并没有阻止你编译(并且它在编译过程中没有产生错误或警告),他们仍然可以声称 VC++ 是兼容的。

当然,我希望他们不会采取这种策略,因为他们非常有兴趣确保他们的工具是最好的。


你应该采取的second步骤是质疑为什么你想要以那种方式做你正在做的事情。您所拥有的似乎是一个基于掩码的简单字节顺序切换器。声明:

res.c[(uint8_t)(mask.c[0])] = (uint8_t)p[0];

无论如何都是有问题的,因为

(uint8_t)(mask.c[0])
可能会评估出大于 3 的值,并且在这种情况下,您将在工会结束后继续写作。

您可能认为确保

mask
没有大于
3
的字节可能会阻止这种情况,但分析器可能不知道这一点。无论如何,已经有很多方法可以切换字节顺序,例如使用
htons
函数系列,或者,由于您的东西无论如何都是硬编码的,只需使用以下方法之一:

res.c[0] = p[0]; res.c[1] = p[1]; res.c[2] = p[2]; res.c[3] = p[3];

或:

res.c[0] = p[3]; res.c[1] = p[2]; res.c[2] = p[1]; res.c[3] = p[0];

或其他东西,用于奇怪的字节排序要求。使用这种方法根本不会引起分析仪的任何抱怨。


如果你真的想用当前的

mask
方法来做,你可以通过暂时抑制它(一行)来删除分析器警告(至少在我正在使用的VS2013中):

#pragma warning(suppress : 6386)
res.c[mask.c[0]] = p[0];
res.c[mask.c[1]] = p[1];
res.c[mask.c[2]] = p[2];
res.c[mask.c[3]] = p[3];

(由于类型已经正确,因此删除了强制转换)。


0
投票

C6386
是不公平的警告,它一直困扰着我,直到我发现一个有趣的解决方案(Visual Studio Community 2019):不要让你的代码过载凌乱的
#pragma
s,尝试替换以下行:

res.c[(uint8_t)(mask.c[0])] = (uint8_t)p[0]; // <-- this line gives C6386
res.c[(uint8_t)(mask.c[1])] = (uint8_t)p[1];
res.c[(uint8_t)(mask.c[2])] = (uint8_t)p[2];
res.c[(uint8_t)(mask.c[3])] = (uint8_t)p[3];

具有以下内容:

*(res.c + (uint8_t)(mask.c[0])) = (uint8_t)p[0]; // no more buggy warnings!
*(res.c + (uint8_t)(mask.c[1])) = (uint8_t)p[1];
*(res.c + (uint8_t)(mask.c[2])) = (uint8_t)p[2];
*(res.c + (uint8_t)(mask.c[3])) = (uint8_t)p[3];

通常在赋值中的内存表达式取值使用括号时出现警告。当它是来源时,你仍然可以使用括号

[]
。如果在其他版本的 VS 中有效,请发表评论。享受吧!

© www.soinside.com 2019 - 2024. All rights reserved.