pvs-stdio ue4
Hi! Though the 2019 conference season is not over yet, we'd like to talk about the bug-finding challenges we offered to visitors at our booth during the past conferences. Starting with the fall of 2019, we've been bringing a new set of challenges, so we can now reveal the solutions to the previous tasks of 2018 and the first half of 2019 – after all, many of them came from previously posted articles, and we had a link or QR code with information about the respective articles printed on our challenge leaflets.
嗨! 尽管2019年会议季节尚未结束,但我们想谈一谈过去会议期间我们在展位上为访客提供的Bug查找挑战。 从2019年秋天开始,我们带来了一系列新的挑战,因此我们现在可以揭示针对2018年以前的任务和2019年上半年的解决方案-毕竟,其中许多来自以前发布的文章,并且我们有一个链接或QR码,其中包含有关打印在我们的挑战传单上的各个文章的信息。
If you attended events where we participated with a booth, you probably saw or even tried to solve some of our challenges. These are snippets of code from real open-source projects written in C, C++, C#, or Java. Each snippet contains a bug, and the guests are challenged to try to find it. A successful solution (or simply participation in the discussion of the bug) is rewarded with a prize: a spiral-bound desktop status, a keychain, and the like:
如果您参加了我们通过展位参加的活动,您可能会看到甚至试图解决我们的一些挑战。 这些是使用C,C ++,C#或Java编写的真实开源项目的代码片段。 每个摘录都包含一个错误,并且要求来宾尝试找到它。 成功的解决方案(或只是参与错误的讨论)将获得奖励:螺旋绑定的桌面状态,钥匙串等:
Want some too? Then welcome to drop by our booth at the upcoming events.
也要吗 然后欢迎在即将举行的活动中到我们的展位前来。
By the way, in the articles "Conference Time! Summing up 2018" and "Conferences. Sub-totals for the first half of 2019", we share our experience of participating in the events held earlier this year and in 2018.
顺便说一下,在“ 会议时间!总结2018 ”和“ 会议。2019年上半年小计 ”一文中,我们分享了参加今年年初和2018年举办的活动的经验。
Okay, let's play our «Find the bug» game. First we'll take a look at the earlier challenges of 2018, grouped by language.
好的,让我们玩“发现错误”游戏。 首先,我们将按语言分组分析2018年早期的挑战。
2018年 (2018)
C ++ (C++)
Chrome虫 (Chromium bug)
static const int kDaysInMonth[13] = {
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
bool ValidateDateTime(const DateTime& time) {
if (time.year < 1 || time.year > 9999 ||
time.month < 1 || time.month > 12 ||
time.day < 1 || time.day > 31 ||
time.hour < 0 || time.hour > 23 ||
time.minute < 0 || time.minute > 59 ||
time.second < 0 || time.second > 59) {
return false;
}
if (time.month == 2 && IsLeapYear(time.year)) {
return time.month <= kDaysInMonth[time.month] + 1;
} else {
return time.month <= kDaysInMonth[time.month];
}
}
解 (Solution)
if (time.month == 2 && IsLeapYear(time.year)) {
return time.month <= kDaysInMonth[time.month] + 1; // <= day
} else {
return time.month <= kDaysInMonth[time.month]; // <= day
}
The body of the last If-else block contains typos in the return statements: time.month was accidentally written for a second time instead of time.day. This mistake makes the function return true all the time. The bug is discussed in detail in the article "February 31" and is a cool example of a bug that isn't easily spotted by code review. This case is also a good demonstration of how we use dataflow analysis.
最后一个If-else块的主体在return语句中包含错别字: time.month是第二次而不是time.day意外地写入了。 该错误使函数始终返回true 。 该错误将在文章“ 2月31日 ”中详细讨论,并且是一个很酷的错误示例,该错误很难被代码审查发现。 这个案例也很好地说明了我们如何使用数据流分析。
虚幻引擎错误 (Unreal Engine bug)
bool VertInfluencedByActiveBone(
FParticleEmitterInstance* Owner,
USkeletalMeshComponent* InSkelMeshComponent,
int32 InVertexIndex,
int32* OutBoneIndex = NULL);
void UParticleModuleLocationSkelVertSurface::Spawn(....)
{
....
int32 BoneIndex1, BoneIndex2, BoneIndex3;
BoneIndex1 = BoneIndex2 = BoneIndex3 = INDEX_NONE;
if(!VertInfluencedByActiveBone(
Owner, SourceComponent, VertIndex[0], &BoneIndex1) &&
!VertInfluencedByActiveBone(
Owner, SourceComponent, VertIndex[1], &BoneIndex2) &&
!VertInfluencedByActiveBone(
Owner, SourceComponent, VertIndex[2]) &BoneIndex3)
{
....
}
解 (Solution)
if (!foo(....) && !foo(....) && !foo(....) & arg)
The bug is now clearly visible. Because of the typo, the third call of the VertInfluencedByActiveBone() function is performed with three arguments instead of four, with the return value then participating in a & operation (bitwise AND: the left operand is the value of type bool returned by VertInfluencedByActiveBone(), and the right operand is the integer variable BoneIndex3). The code is still compilable. This is the fixed version (a comma added, the closing parenthesis moved to the end of the expression):
该错误现在清晰可见。 由于存在拼写错误,对VertInfluencedByActiveBone()函数的第三次调用使用三个参数而不是四个参数执行,返回值然后参与&运算(按位AND:左操作数是VertInfluencedByActiveBone()返回的布尔类型的值) ,右边的操作数是整数变量BoneIndex3 )。 该代码仍然可以编译。 这是固定版本(添加逗号,右括号移到表达式的末尾):
if(!VertInfluencedByActiveBone(
Owner, SourceComponent, VertIndex[0], &BoneIndex1) &&
!VertInfluencedByActiveBone(
Owner, SourceComponent, VertIndex[1], &BoneIndex2) &&
!VertInfluencedByActiveBone(
Owner, SourceComponent, VertIndex[2], &BoneIndex3))
This error was originally mentioned in the article "A Long-Awaited Check of Unreal Engine 4", where it was titled «the nicest error», which I totally agree with.
该错误最初是在文章“ 对虚幻引擎4的期待已久的检查 ”中提到的,其标题为“最好的错误”,我完全同意。
Android错误 (Android bugs)
void TagMonitor::parseTagsToMonitor(String8 tagNames) {
std::lock_guard<std::mutex> lock(mMonitorMutex);
// Expand shorthands
if (ssize_t idx = tagNames.find("3a") != -1) {
ssize_t end = tagNames.find(",", idx);
char* start = tagNames.lockBuffer(tagNames.size());
start[idx] = '\0';
....
}
....
}
解 (Solution)
if (ssize_t idx = (tagNames.find("3a") != -1))
The idx variable will be assigned the value 0 or 1, and whether the condition is true or false will depend on this value, which is a mistake. This is the fixed version:
将为idx变量分配值0或1,条件是真还是假将取决于此值,这是一个错误。 这是固定版本:
ssize_t idx = tagNames.find("3a");
if (idx != -1)
This bug was mentioned in the article "We Checked the Android Source Code by PVS-Studio, or Nothing is Perfect".
在文章“ 我们通过PVS-Studio检查了Android源代码,或者没有完美的方法 ”中提到了此错误。
Here's another non-trivial challenge with an Android bug:
这是另一个带有Android错误的重要挑战:
typedef int32_t GGLfixed;
GGLfixed gglFastDivx(GGLfixed n, GGLfixed d)
{
if ((d>>24) && ((d>>24)+1)) {
n >>= 8;
d >>= 8;
}
return gglMulx(n, gglRecip(d));
}
解 (Solution)
The programmer wanted to check that the 8 most significant bits of the d variable are set to 1 but not all of them at a time. In other words, they wanted to check that the most significant byte stores any value except 0x00 and 0xFF. First the programmer checks the most significant bits for null using the (d>>24) expression. Then they shift the eight most significant bits to the least significant byte, expecting the most significant sign bit to get duplicated in all the other bits. That is, if the d variable has the value 0b11111111'00000000'00000000'00000000, it will turn into 0b11111111'11111111'11111111'11111111 after the shift. By adding 1 to the int value 0xFFFFFFFF, the programmer is expecting to get 0 (-1+1=0). Thus, the ((d>>24)+1) expression is used to check that not all of the eight most significant bits are set to 1.
程序员希望检查d变量的8个最高有效位是否设置为1,但不是一次都设置为全部。 换句话说,他们想检查最高有效字节是否存储除0x00和0xFF之外的任何值。 首先,程序员使用(d >> 24)表达式检查最高有效位是否为空。 然后,他们将八个最高有效位移至最低有效字节,并期望最高有效符号位在所有其他位中重复。 也就是说,如果d变量的值为0b11111111'00000000'00000000'00000000,则在移位后它将变为0b11111111'11111111'11111111'11111111。 通过将1与int值0xFFFFFFFF相加,程序员期望得到0(-1 + 1 = 0)。 因此, ((d >> 24)+1)表达式用于检查不是所有八个最高有效位都被设置为1。
However, the most significant sign bit does not necessarily get «spread» when shifted. This is what the standard says: «The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a non-negative value, the value of the result is the integral part of the quotient of E1/2^E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined».
但是,最高有效符号位在移位时不一定会得到“扩展”。 这就是标准所说的:«E1 >> E2的值是E1右移E2位的位置。 如果E1具有无符号类型,或者E1具有带符号类型和非负值,则结果的值是E1 / 2 ^ E2商的整数部分。 如果E1具有带符号的类型和负值,则结果值是实现定义的 »。
So, this is an example of implementation-defined behavior. How exactly this code will work depends on the CPU architecture and compiler implementation. The most significant bits may well end up as zeroes after the shift, and the ((d>>24)+1) expression would then always return a value other than 0, i.e. an always true value.
因此,这是实现定义的行为的示例。 此代码的确切工作方式取决于CPU架构和编译器实现。 移位后,最高有效位很可能最终为零,并且((d >> 24)+1)表达式将始终返回除0以外的值,即始终为真值。
That, indeed, is a non-trivial challenge. Like the previous bug, this one was originally discussed in the article "We Checked the Android Source Code by PVS-Studio, or Nothing is Perfect".
确实,这是一个不平凡的挑战。 像先前的错误一样,该错误最初在文章“ 我们通过PVS-Studio检查了Android源代码,或者Nothing is Perfect ”中进行了讨论。
2019年 (2019)
C ++ (C++)
«这都是海湾合作委员会的错» («It's all GCC's fault»)
int foo(const unsigned char *s)
{
int r = 0;
while(*s) {
r += ((r * 20891 + *s *200) | *s ^ 4 | *s ^ 3) ^ (r >> 1);
s++;
}
return r & 0x7fffffff;
}
The programmer blames the GCC 8 compiler for the bug. Is it really GCC's fault?
程序员将此错误归咎于GCC 8编译器。 真的是海湾合作委员会的错吗?
解 (Solution)
This error was described in the article "PVS-Studio 6.26 Released".
在文章“ PVS-Studio 6.26已发布 ”中描述了此错误。
QT错误 (QT bug)
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
bool QMetaEnum::isFlag() const
{
const int offset = priv(mobj->d.data)->revision >= 8 ? 2 : 1;
return mobj && mobj->d.data[handle + offset] & EnumIsFlag;
}
解 (Solution)
The bug was mentioned in the article "A Third Check of Qt 5 with PVS-Studio".
在“ 使用PVS-Studio对Qt 5进行第三次检查 ” 一文中提到了该错误。
C# (C#)
Infer.NET错误 (Infer.NET bug)
public static void
WriteAttribute(TextWriter writer,
string name,
object defaultValue,
object value,
Func<object, string> converter = null)
{
if ( defaultValue == null && value == null
|| value.Equals(defaultValue))
{
return;
}
string stringValue = converter == null ? value.ToString() :
converter(value);
writer.Write($"{name}=\"{stringValue}\" ");
}
解 (Solution)
This bug is from the article "What Errors Lurk in Infer.NET Code?"
此错误来自文章“ Infer.NET代码中有什么错误潜伏? ”
FastReport错误 (FastReport bug)
public class FastString
{
private const int initCapacity = 32;
private void Init(int iniCapacity)
{ sb = new StringBuilder(iniCapacity); .... }
public FastString() { Init(initCapacity); }
public FastString(int iniCapacity) { Init(initCapacity); }
public StringBuilder StringBuilder => sb;
}
....
Console.WriteLine(new FastString(256).StringBuilder.Capacity);
What will the program output in the console? What's wrong with the FastString class?
程序将在控制台中输出什么? FastString类怎么了?
解 (Solution)
public FastString(int iniCapacity){ Init(initCapacity); }
The constructor parameter iniCapacity won't be used; what gets passed instead is the constant initCapacity.
不会使用构造函数参数iniCapacity ; 相反,传递的是常量initCapacity 。
The bug was discussed in the article "The Fastest Reports in the Wild West — and a Handful of Bugs..."
该错误已在“ 荒野西部最快的报告-以及少数错误... ” 一文中进行了讨论。
罗斯林错误 (Roslyn bug)
private SyntaxNode GetNode(SyntaxNode root)
{
var current = root;
....
while (current.FullSpan.Contains(....))
{
....
var nodeOrToken = current.ChildThatContainsPosition(....);
....
current = nodeOrToken.AsNode();
}
....
}
public SyntaxNode AsNode()
{
if (_token != null)
{
return null;
}
return _nodeOrParent;
}
解 (Solution)
This bug is from the article "Checking the Roslyn Source Code".
此错误来自文章“ 检查Roslyn源代码 ”。
Unity错误 (Unity bug)
....
staticFields = packedSnapshot.typeDescriptions
.Where(t =>
t.staticFieldBytes != null &
t.staticFieldBytes.Length > 0)
.Select(t => UnpackStaticFields(t))
.ToArray()
....
解 (Solution)
This bug was originally shown in the article "Discussing Errors in Unity3D's Open-Source Components".
此错误最初在文章“ 讨论Unity3D的开源组件中的错误 ”中显示。
Java (Java)
IntelliJ IDEA错误 (IntelliJ IDEA bug)
private static boolean checkSentenceCapitalization(@NotNull String value) {
List<String> words = StringUtil.split(value, " ");
....
int capitalized = 1;
....
return capitalized / words.size() < 0.2; // allow reasonable amount of
// capitalized words
}
Why does the program incorrectly calculate the number of capitalized words?
为什么程序会错误地计算大写单词的数量?
解 (Solution)
This bug is from the article "PVS-Studio for Java".
该错误来自文章“ PVS-Studio for Java ”。
SpotBugs错误 (SpotBugs bug)
public static String getXMLType(@WillNotClose InputStream in) throws IOException
{
....
String s;
int count = 0;
while (count < 4) {
s = r.readLine();
if (s == null) {
break;
}
Matcher m = tag.matcher(s);
if (m.find()) {
return m.group(1);
}
}
throw new IOException("Didn't find xml tag");
....
}
What's wrong with the search of the xml tag?
搜索xml标记有什么问题?
解 (Solution)
Like the previous bug, this one was described in the article "PVS-Studio for Java".
像以前的错误一样,该错误在文章“ PVS-Studio for Java ”中进行了描述。
That's all for today. Come see us at the upcoming events – look for the unicorn. We'll be offering new interesting challenges and, of course, giving prizes. See you!
今天就这些。 快来看我们即将举行的活动–寻找独角兽。 我们将提供新的有趣挑战,当然还有奖品。 再见!
pvs-stdio ue4