SlideShare a Scribd company logo
Hypercritical C++ Code Review
Yuri Minaev
minev@viva64.com
2
A C++ developer at PVS-
Studio.
Working on the core
functionality and diagnostic
rules of the C/C++ static code
analyzer.
About me
3
• We all do code reviews
• Who doesn't admit this – does it twice as often
• It's ok, nobody's gonna blame you
• Just make sure, you take precautions
What is this about?
4
• We all do code reviews
• Who doesn't admit this –
does it twice as often
• It's ok, nobody's gonna blame
you
• Just make sure, you take
precautions
What are you talking about?
5
Aut'o'matic
6
void foo(const std::vector<....> &vec)
{
for (auto i = 0; i < vec.size(); ++i)
{
// do some magic with vec[i]
}
}
int i = 0;
7
void foo(const std::vector<....> &vec)
{
for (auto i = 0; i < vec.size(); ++i)
{
// do some magic with vec[i]
}
}
Bad for x64
8
void foo(const std::vector<....> &vec)
{
for (auto i = 0; i < vec.size(); ++i)
{
// do some magic with vec[i]
}
} Signed/unsigned
mixup
9
void foo(const std::vector<....> &vec)
{
for (size_t i = 0; i < vec.size(); ++i)
{
// do some magic with vec[i]
}
}
Better
10
void foo(const std::vector<....> &vec)
{
for (auto i = 0ull; i < vec.size(); ++i)
{
// do some magic with vec[i]
}
}
128-bit systems, anyone?
11
void foo(const std::vector<....> &vec)
{
for (auto&& item : vec)
{
// do some magic with item
}
}
Look, I fixed it
12
Misreference
13
auto other =
static_cast<const Self &>(rhs_);
const T &a = data[n];
const T &b = other.data[m];
// Do stuff
:(
14
auto& other =
static_cast<const Self &>(rhs_);
const T &a = data[n];
const T &b = other.data[m];
// Do stuff
Look, I fixed it
15
decltype(auto) other =
static_cast<const Self &>(rhs_);
const T &a = data[n];
const T &b = other.data[m];
// Do stuff
If you're really into it
16
Thou shalt not auto, unless thy faith is strong and
pure
17
18
Versus Intuition
19
using V = std::vector<....>;
void vector_inc(V &v)
{
for (size_t i = 0; i < v.size(); i++)
{
v[i]++;
}
}
20
for (size_t i = 0; i < v.size(); i++)
{
v[i]++;
}
std::vector<uint32_t> &v;
std::vector<uint8_t> &v;
Which is faster?
21
Let's benchmark, shall we?
Compiler Element -O1 -O2 -O3
gcc 8 uint8_t 2.0 2.0 2.0
gcc 8 uint32_t 2.3 1.3 0.2
clang 8 uint8_t 9.2 2.0 2.0
clang 8 uint32_t 9.2 0.2 0.2
22
23
24
25
// using V = std::vector<uint8_t>;
auto it = v.begin();
const auto end = v.end();
for (; it != end; ++it)
{
++(*it);
}
26
27
One more (with uint8_t)
Compiler Before (-O2) After (-O2) Speedup
gcc 8 2.0 1.3 1.5x
clang 8 2.0 0.06 33.4x
28
auto it = v.begin();
const auto end = v.end();
for (; it != end; ++it)
{
++(*it);
}
Does it remind you of anything?
29
for (auto&& elem : v)
{
++elem;
}
How about this?
30
31
Thou shalt not write indexed loops for they are
abomination before the Code
32
33
Privacy Matters
34
void InputPassword(char *pswd);
void ProcessPassword(const char *pswd);
void DoSomething()
{
char password[MAX_PASSWORD_LEN];
InputPassword(password);
ProcessPassword(password);
memset(password, 0, sizeof(password));
}
35
What does the compiler say?
clang 10 with –O2
36
Looks contrived?
37
• Custom safe_memset + disabled LTO/WPO
• Access a non-volatile object through a volatile pointer
• Call memset through a volatile function pointer
• Volatile assembly code
• Memset + memory barrier
• Disable compiler optimisations (-fno-builtin-memset)
• C11: memset_s
So, what can you do?
38
Thou shalt wash thy data thoroughly before releasing
it
39
Unwashed Data
40
if (!fgets(readbuf, BUFSIZ, stdin))
{
// ....
}
if(readbuf[strlen(readbuf) - 1] == 'n')
readbuf[strlen(readbuf) - 1] = '0';
CVE-2015-8948
41
if (!fgets(readbuf, BUFSIZ, stdin))
{
// ....
}
if(readbuf[strlen(readbuf) - 1] == 'n')
readbuf[strlen(readbuf) - 1] = '0';
Put an empty line here
This goes BOOM
42
if (getline(&line, &linelen, stdin)
== -1)
{
// ....
}
if(line[strlen(line) - 1] == 'n')
line[strlen(line) - 1] = '0';
Look, I fixed it
43
if (getline(&line, &linelen, stdin)
== -1)
{
// ....
}
if(line[strlen(line) - 1] == 'n')
line[strlen(line) - 1] = '0';
CVE-2016-6262
44
if (getline(&line, &linelen, stdin)
== -1)
{
// ....
}
if(line[strlen(line) - 1] == 'n')
line[strlen(line) - 1] = '0';
Put an empty line here
This goes BOOM
45
Thou shalt not accept data from strangers for they
might be sinful
46
Last Mile
47
void Init( float ix=0, float iy=0,
float iz=0, float iw=0 )
{
SetX( ix );
SetY( iy );
SetZ( iz );
SetZ( iw );
}
SetW( iw );
48
if (access & FILE_WRITE_ATTRIBUTES)
output.append("tFILE_WRITE_ATTRIBUTESn");
if (access & FILE_WRITE_DATA)
output.append("tFILE_WRITE_DATAn");
if (access & FILE_WRITE_EA)
output.append("tFILE_WRITE_EAn");
if (access & FILE_WRITE_EA)
output.append("tFILE_WRITE_EAn");
Same blocks
49
if (
protocol.EqualsIgnoreCase("http") ||
protocol.EqualsIgnoreCase("https") ||
protocol.EqualsIgnoreCase("news") ||
protocol.EqualsIgnoreCase("ftp") ||
protocol.EqualsIgnoreCase("file") ||
protocol.EqualsIgnoreCase("javascript") ||
protocol.EqualsIgnoreCase("ftp")
) {
Double checking
50
Thou shalt not copy-paste thy code blocks
51
52
Have Spaceship, Will Travel
<=>
53
struct Foo
{
int a, b;
};
bool operator==(Foo lhs, Foo rhs)
{
return lhs.a == rhs.a
&& lhs.b == rhs.b;
}
54
struct Foo
{
int a, b;
};
bool operator!=(Foo lhs, Foo rhs)
{
return !(lhs == rhs);
}
bool operator==(Foo lhs, Foo rhs)
{
return lhs.a == rhs.a && lhs.b == rhs.b;
}
So far so good
55
bool operator<(Foo lhs, Foo rhs) { ??? }
bool operator<=(Foo lhs, Foo rhs) { ??? }
bool operator>(Foo lhs, Foo rhs) { ??? }
bool operator>=(Foo lhs, Foo rhs) { ??? }
How about these?
56
bool operator<(Foo lhs, Foo rhs)
{
return lhs.a < rhs.a
&& lhs.b < rhs.b;
}
So far so good
57
bool operator<(Foo lhs, Foo rhs)
{
return lhs.a < rhs.a
&& lhs.b < rhs.b;
}
Foo { 2, 1 } < Foo { 1, 2 }
Foo { 1, 2 } < Foo { 2, 1 }
false
false
58
bool operator<(Foo lhs, Foo rhs)
{
if (lhs.a < rhs.a) return true;
if (rhs.a < lhs.a) return false;
return lhs.b < rhs.b;
}
Foo { 2, 1 } < Foo { 1, 2 }
Foo { 1, 2 } < Foo { 2, 1 }
false
true
59
struct Foo
{
double a;
};
bool operator<(Foo lhs, Foo rhs)
{
return lhs.a < rhs.a;
}
bool operator>=(Foo lhs, Foo rhs)
{
return !(lhs < rhs);
}
60
Foo { 1.0 } < Foo { 2.0 }
Foo { 1.0 } < Foo { NaN }
true
false
bool operator<(Foo lhs, Foo rhs)
{
return lhs.a < rhs.a;
}
bool operator>=(Foo lhs, Foo rhs)
{
return !(lhs < rhs);
}
Foo { 1.0 } >= Foo { NaN } true
61
So, what shall we do?
struct Foo
{
// anything
auto operator<=>(const Foo &rhs) const = default;
};
62
Foo { 1.0 } < Foo { 2.0 }
Foo { 1.0 } < Foo { NaN }
true
false
struct Foo
{
// anything
auto operator<=>(const Foo &rhs) const = default;
};
Foo { 1.0 } >= Foo { NaN } false
Foo { 2, 1 } < Foo { 1, 2 }
Foo { 1, 2 } < Foo { 2, 1 }
false
true
63
While we're at it
64
const Ptree* pIf =
IsA(p, ntIfStatement, ntSwitchStatement)
? p
: IsA(First(p),
ntIfStatement, ntSwitchStatement)
&& ContainsNoReturnStatements(First(p))
? First(p)
: nullptr;
65
const Ptree* pIf =
IsA(p, ntIfStatement, ntSwitchStatement)
? p
: IsA(First(p),
ntIfStatement, ntSwitchStatement)
&& ContainsNoReturnStatements(First(p))
? First(p)
: nullptr;
66
Thy comparison routines shall be correct or else the
Wrath of Code will get thee
67
Don't Push on Me
68
struct G584_Info
{
G584_Info(/*A bunch of params*/)
{/**/}
const Ptree *m_p;
bool m_add, m_mul;
bool m_sub, m_div;
};
69
auto what = p->What();
if (what == ntParenExpr)
{
// infs is std::vector<G584_Info>&
infs.push_back(
G584_Info(p, true, true, false, false)
);
p = SafeSkipParentesis(p);
} Possible copy
70
auto what = p->What();
if (what == ntParenExpr)
{
// infs is std::vector<G584_Info>&
infs.emplace_back(
p, true, true, false, false
);
p = SafeSkipParentesis(p);
} Better
71
struct G584_Info
{
G584_Info(/*A bunch of params*/)
{/**/}
const Ptree *m_p;
bool m_add, m_mul;
bool m_sub, m_div;
};
emplace_back this?
yes, since C++20
72
Thou shalt not push that which can be emplaced
73
Find It Again
74
auto&& infoMap = GetFunctionDangerousInfoMap();
auto it = infoMap.find(funcInfo);
if (it == infoMap.end())
{
infoMap.insert(
std::make_pair(funcInfo, dangerousInfo));
}
else
{
auto&& a = it->second;
}
Here we go again
75
auto it = infoMap.find(funcInfo);
if (it == infoMap.end())
{
infoMap.emplace(funcInfo, dangerousInfo);
}
else
{
auto&& a = it->second;
}
Better?
76
auto it = infoMap.find(funcInfo);
if (it == infoMap.end())
{
infoMap.emplace(funcInfo,
dangerousInfo);
}
else
{
auto&& a = it->second;
} Double lookup
77
if (auto [it, success] =
infoMap.try_emplace(funcInfo,
dangerousInfo);
!success)
{
auto&& a = it->second;
}
Look, I fixed it
78
Thou shalt search only once
79
80
Mind The Sign
81
gcc is <ILLEGIBLE> broken
As I understand it, those <BEEP>ing <BAD PEOPLE>
decided to <ILLEGIBLE> break everything again. It
worked before and now it's broken.
For example, dropping the sign (a & 0x7fffffff)
doesn't <BLEEP>ing work, and nothing works no
more.
They always <FLIP>ing break everything, those
<CENSORED>s. Take your <WEEP>ing UB and <...>
82
int foo(const char *s)
{
int r = 0;
while (*s)
{
r += ((r * 20891 + *s * 200)
| *s ^ 4 | *s ^ 3 )
^ (r >> 1);
s++;
}
return r & 0x7fffffff;
}
Signed
Overflow
Drop the sign
83
foo(char const*):
movzx edx, BYTE PTR [rdi]
test dl, dl
je .L4
xor esi, esi
.L3:
; lots of calculations
jne .L3
mov eax, esi
and eax, 2147483647
ret
.L4:
xor eax, eax
ret
Drop the sign
84
int foo(const char *s)
{
int r = 0;
while (*s)
{
r += ((r * 20891 + *s * 200)
| *s ^ 4 | *s ^ 3 )
^ (r >> 1);
s++;
}
return r & 0x7fffffff;
}
gcc -O2 -std=c++17 -funsigned-char
85
foo(char const*):
movzx edx, BYTE PTR [rdi]
xor r8d, r8d
test dl, dl
je .L1
.L3:
; lots of calculations
jne .L3
.L1:
mov eax, r8d
ret
Oops
86
Thou shalt not cook signed values with overflow
semantics
87
Throwing Out
noexcept(BOOM)
88
void func() noexcept
{
// ....
throw SomeException{};
}
This is REALLY bad
89
void func() noexcept
{
anotherFunc();
}
This is also REALLY bad
void anotherFunc()
{
throw SomeException{};
}
90
Not noexcept, but implies so
DllMain
91
BOOL WINAPI DllMain(/**/)
{
BOOL br = TRUE;
// ....
if (FAILED(DllRegisterServer()))
br = FALSE;
// ....
return br;
}
92
BOOL WINAPI DllMain(/**/)
{
BOOL br = TRUE;
// ....
if (FAILED(DllRegisterServer()))
br = FALSE;
// ....
return br;
}
PVS-Studio: don't throw from
DllMain, mate
93
BOOL WINAPI DllMain(/**/)
{
BOOL br = TRUE;
// ....
if (FAILED(DllRegisterServer()))
br = FALSE;
// ....
return br;
}
PVS-Studio: don't throw from
DllMain, mate
Me: LOLWUT?
94
• Part of Window API
• Essentially, written in C
• Related to COM
• Doesn't throw
DllRegisterServer
Looks buggy
Or does it?
95
HRESULT WINAPI DllRegisterServer(VOID)
{
// ....
hr = ::RegOpenKeyEx(/**/);
// ....
DllGetObjectInfo(/**/);
// ....
hr = ::RegSetValueEx(/**/);
// ....
RegCloseKey(hk);
}
These don't throw
96
HRESULT WINAPI DllGetObjectInfo(/**/)
{
// ....
hr = DllGetObject(/**/);
if (SUCCEEDED(hr))
{
// ....
delete pPlugin;
}
// ....
}
97
HRESULT WINAPI DllGetObject(
DWORD dwPluginId,
IShellPlugin **ppPlugin)
{
// ....
*ppPlugin = new CCommandPlugin;
// ....
}
98
He who is without noexcept shall throw, and none
other
QUESTIONS
99

More Related Content

PDF
Arduino coding class
PDF
Arduino coding class part ii
PPTX
Java JIT Optimization Research
PDF
Playing with camera preview buffers on BlackBerry 10
PPTX
PVS-Studio team experience: checking various open source projects, or mistake...
DOCX
Design A Screen saver in c on Moving Train with Side view.
DOCX
Graphics programs
PDF
第二回 冬のスイッチ大勉強会 - FullColorLED & MPU-6050編 -
Arduino coding class
Arduino coding class part ii
Java JIT Optimization Research
Playing with camera preview buffers on BlackBerry 10
PVS-Studio team experience: checking various open source projects, or mistake...
Design A Screen saver in c on Moving Train with Side view.
Graphics programs
第二回 冬のスイッチ大勉強会 - FullColorLED & MPU-6050編 -

What's hot (19)

PPT
Евгений Крутько, Многопоточные вычисления, современный подход.
PDF
第一回 冬のスイッチ大勉強会 - XBee編 -
PDF
Anomalies in X-Ray Engine
PDF
Explanations to the article on Copy-Paste
TXT
Layer 2221 1 Subidazbuka
PDF
How to not write a boring test in Golang
DOCX
Advance java
DOCX
NVT MD
KEY
openFrameworks 007 - 3D
PDF
Антон Бикинеев, Writing good std::future&lt; C++ >
PDF
Java, Up to Date Sources
DOC
COMPUTER GRAPHICS LAB MANUAL
ZIP
Learning from 6,000 projects mining specifications in the large
PDF
Kirk Shoop, Reactive programming in C++
PDF
ipython notebook poc memory forensics
PPTX
Pro typescript.ch03.Object Orientation in TypeScript
PDF
Welcome to Modern C++
DOCX
Graphics practical lab manual
DOC
SE Computer, Programming Laboratory(210251) University of Pune
Евгений Крутько, Многопоточные вычисления, современный подход.
第一回 冬のスイッチ大勉強会 - XBee編 -
Anomalies in X-Ray Engine
Explanations to the article on Copy-Paste
Layer 2221 1 Subidazbuka
How to not write a boring test in Golang
Advance java
NVT MD
openFrameworks 007 - 3D
Антон Бикинеев, Writing good std::future&lt; C++ >
Java, Up to Date Sources
COMPUTER GRAPHICS LAB MANUAL
Learning from 6,000 projects mining specifications in the large
Kirk Shoop, Reactive programming in C++
ipython notebook poc memory forensics
Pro typescript.ch03.Object Orientation in TypeScript
Welcome to Modern C++
Graphics practical lab manual
SE Computer, Programming Laboratory(210251) University of Pune
Ad

Similar to Hypercritical C++ Code Review (20)

PDF
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
PDF
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
PDF
Checking Clang 11 with PVS-Studio
PDF
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 1
PDF
Analysis of Godot Engine's Source Code
PPTX
C++ Code as Seen by a Hypercritical Reviewer
PDF
How to make a large C++-code base manageable
PDF
How to avoid bugs using modern C++
PDF
A Spin-off: CryEngine 3 SDK Checked with CppCat
PDF
Tesseract. Recognizing Errors in Recognition Software
PPTX
Static analysis of C++ source code
PPTX
Static analysis of C++ source code
PDF
Grounded Pointers
PDF
PVS-Studio delved into the FreeBSD kernel
PDF
Secure Programming Practices in C++ (NDC Security 2018)
PDF
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
PPTX
A scrupulous code review - 15 bugs in C++ code
PPTX
What has to be paid attention when reviewing code of the library you develop
PDF
100 bugs in Open Source C/C++ projects
PDF
Zero, one, two, Freddy's coming for you
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
"Why is there no artificial intelligence yet?" Or, analysis of CNTK tool kit ...
Checking Clang 11 with PVS-Studio
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 1
Analysis of Godot Engine's Source Code
C++ Code as Seen by a Hypercritical Reviewer
How to make a large C++-code base manageable
How to avoid bugs using modern C++
A Spin-off: CryEngine 3 SDK Checked with CppCat
Tesseract. Recognizing Errors in Recognition Software
Static analysis of C++ source code
Static analysis of C++ source code
Grounded Pointers
PVS-Studio delved into the FreeBSD kernel
Secure Programming Practices in C++ (NDC Security 2018)
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
A scrupulous code review - 15 bugs in C++ code
What has to be paid attention when reviewing code of the library you develop
100 bugs in Open Source C/C++ projects
Zero, one, two, Freddy's coming for you
Ad

More from Andrey Karpov (20)

PDF
60 антипаттернов для С++ программиста
PDF
60 terrible tips for a C++ developer
PPTX
Ошибки, которые сложно заметить на code review, но которые находятся статичес...
PDF
PVS-Studio in 2021 - Error Examples
PDF
PVS-Studio in 2021 - Feature Overview
PDF
PVS-Studio в 2021 - Примеры ошибок
PDF
PVS-Studio в 2021
PPTX
Make Your and Other Programmer’s Life Easier with Static Analysis (Unreal Eng...
PPTX
Best Bugs from Games: Fellow Programmers' Mistakes
PPTX
Does static analysis need machine learning?
PPTX
Typical errors in code on the example of C++, C#, and Java
PPTX
How to Fix Hundreds of Bugs in Legacy Code and Not Die (Unreal Engine 4)
PPTX
Game Engine Code Quality: Is Everything Really That Bad?
PPTX
The Use of Static Code Analysis When Teaching or Developing Open-Source Software
PPTX
Static Code Analysis for Projects, Built on Unreal Engine
PPTX
Safety on the Max: How to Write Reliable C/C++ Code for Embedded Systems
PPTX
The Great and Mighty C++
PPTX
Static code analysis: what? how? why?
PDF
PVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOps
PDF
PVS-Studio Static Analyzer as a Tool for Protection against Zero-Day Vulnerab...
60 антипаттернов для С++ программиста
60 terrible tips for a C++ developer
Ошибки, которые сложно заметить на code review, но которые находятся статичес...
PVS-Studio in 2021 - Error Examples
PVS-Studio in 2021 - Feature Overview
PVS-Studio в 2021 - Примеры ошибок
PVS-Studio в 2021
Make Your and Other Programmer’s Life Easier with Static Analysis (Unreal Eng...
Best Bugs from Games: Fellow Programmers' Mistakes
Does static analysis need machine learning?
Typical errors in code on the example of C++, C#, and Java
How to Fix Hundreds of Bugs in Legacy Code and Not Die (Unreal Engine 4)
Game Engine Code Quality: Is Everything Really That Bad?
The Use of Static Code Analysis When Teaching or Developing Open-Source Software
Static Code Analysis for Projects, Built on Unreal Engine
Safety on the Max: How to Write Reliable C/C++ Code for Embedded Systems
The Great and Mighty C++
Static code analysis: what? how? why?
PVS-Studio Is Now in Chocolatey: Checking Chocolatey under Azure DevOps
PVS-Studio Static Analyzer as a Tool for Protection against Zero-Day Vulnerab...

Recently uploaded (20)

PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
Understanding Forklifts - TECH EHS Solution
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PDF
AI in Product Development-omnex systems
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
Digital Strategies for Manufacturing Companies
PPTX
Essential Infomation Tech presentation.pptx
PPTX
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PPTX
Introduction to Artificial Intelligence
PPTX
Reimagine Home Health with the Power of Agentic AI​
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
top salesforce developer skills in 2025.pdf
PDF
medical staffing services at VALiNTRY
PPTX
ai tools demonstartion for schools and inter college
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PDF
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Understanding Forklifts - TECH EHS Solution
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
AI in Product Development-omnex systems
Navsoft: AI-Powered Business Solutions & Custom Software Development
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Digital Strategies for Manufacturing Companies
Essential Infomation Tech presentation.pptx
Lecture 3: Operating Systems Introduction to Computer Hardware Systems
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
Introduction to Artificial Intelligence
Reimagine Home Health with the Power of Agentic AI​
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
top salesforce developer skills in 2025.pdf
medical staffing services at VALiNTRY
ai tools demonstartion for schools and inter college
How to Choose the Right IT Partner for Your Business in Malaysia
Claude Code: Everyone is a 10x Developer - A Comprehensive AI-Powered CLI Tool

Hypercritical C++ Code Review

  • 1. Hypercritical C++ Code Review Yuri Minaev minev@viva64.com
  • 2. 2 A C++ developer at PVS- Studio. Working on the core functionality and diagnostic rules of the C/C++ static code analyzer. About me
  • 3. 3 • We all do code reviews • Who doesn't admit this – does it twice as often • It's ok, nobody's gonna blame you • Just make sure, you take precautions What is this about?
  • 4. 4 • We all do code reviews • Who doesn't admit this – does it twice as often • It's ok, nobody's gonna blame you • Just make sure, you take precautions What are you talking about?
  • 6. 6 void foo(const std::vector<....> &vec) { for (auto i = 0; i < vec.size(); ++i) { // do some magic with vec[i] } } int i = 0;
  • 7. 7 void foo(const std::vector<....> &vec) { for (auto i = 0; i < vec.size(); ++i) { // do some magic with vec[i] } } Bad for x64
  • 8. 8 void foo(const std::vector<....> &vec) { for (auto i = 0; i < vec.size(); ++i) { // do some magic with vec[i] } } Signed/unsigned mixup
  • 9. 9 void foo(const std::vector<....> &vec) { for (size_t i = 0; i < vec.size(); ++i) { // do some magic with vec[i] } } Better
  • 10. 10 void foo(const std::vector<....> &vec) { for (auto i = 0ull; i < vec.size(); ++i) { // do some magic with vec[i] } } 128-bit systems, anyone?
  • 11. 11 void foo(const std::vector<....> &vec) { for (auto&& item : vec) { // do some magic with item } } Look, I fixed it
  • 13. 13 auto other = static_cast<const Self &>(rhs_); const T &a = data[n]; const T &b = other.data[m]; // Do stuff :(
  • 14. 14 auto& other = static_cast<const Self &>(rhs_); const T &a = data[n]; const T &b = other.data[m]; // Do stuff Look, I fixed it
  • 15. 15 decltype(auto) other = static_cast<const Self &>(rhs_); const T &a = data[n]; const T &b = other.data[m]; // Do stuff If you're really into it
  • 16. 16 Thou shalt not auto, unless thy faith is strong and pure
  • 17. 17
  • 19. 19 using V = std::vector<....>; void vector_inc(V &v) { for (size_t i = 0; i < v.size(); i++) { v[i]++; } }
  • 20. 20 for (size_t i = 0; i < v.size(); i++) { v[i]++; } std::vector<uint32_t> &v; std::vector<uint8_t> &v; Which is faster?
  • 21. 21 Let's benchmark, shall we? Compiler Element -O1 -O2 -O3 gcc 8 uint8_t 2.0 2.0 2.0 gcc 8 uint32_t 2.3 1.3 0.2 clang 8 uint8_t 9.2 2.0 2.0 clang 8 uint32_t 9.2 0.2 0.2
  • 22. 22
  • 23. 23
  • 24. 24
  • 25. 25 // using V = std::vector<uint8_t>; auto it = v.begin(); const auto end = v.end(); for (; it != end; ++it) { ++(*it); }
  • 26. 26
  • 27. 27 One more (with uint8_t) Compiler Before (-O2) After (-O2) Speedup gcc 8 2.0 1.3 1.5x clang 8 2.0 0.06 33.4x
  • 28. 28 auto it = v.begin(); const auto end = v.end(); for (; it != end; ++it) { ++(*it); } Does it remind you of anything?
  • 29. 29 for (auto&& elem : v) { ++elem; } How about this?
  • 30. 30
  • 31. 31 Thou shalt not write indexed loops for they are abomination before the Code
  • 32. 32
  • 34. 34 void InputPassword(char *pswd); void ProcessPassword(const char *pswd); void DoSomething() { char password[MAX_PASSWORD_LEN]; InputPassword(password); ProcessPassword(password); memset(password, 0, sizeof(password)); }
  • 35. 35 What does the compiler say? clang 10 with –O2
  • 37. 37 • Custom safe_memset + disabled LTO/WPO • Access a non-volatile object through a volatile pointer • Call memset through a volatile function pointer • Volatile assembly code • Memset + memory barrier • Disable compiler optimisations (-fno-builtin-memset) • C11: memset_s So, what can you do?
  • 38. 38 Thou shalt wash thy data thoroughly before releasing it
  • 40. 40 if (!fgets(readbuf, BUFSIZ, stdin)) { // .... } if(readbuf[strlen(readbuf) - 1] == 'n') readbuf[strlen(readbuf) - 1] = '0'; CVE-2015-8948
  • 41. 41 if (!fgets(readbuf, BUFSIZ, stdin)) { // .... } if(readbuf[strlen(readbuf) - 1] == 'n') readbuf[strlen(readbuf) - 1] = '0'; Put an empty line here This goes BOOM
  • 42. 42 if (getline(&line, &linelen, stdin) == -1) { // .... } if(line[strlen(line) - 1] == 'n') line[strlen(line) - 1] = '0'; Look, I fixed it
  • 43. 43 if (getline(&line, &linelen, stdin) == -1) { // .... } if(line[strlen(line) - 1] == 'n') line[strlen(line) - 1] = '0'; CVE-2016-6262
  • 44. 44 if (getline(&line, &linelen, stdin) == -1) { // .... } if(line[strlen(line) - 1] == 'n') line[strlen(line) - 1] = '0'; Put an empty line here This goes BOOM
  • 45. 45 Thou shalt not accept data from strangers for they might be sinful
  • 47. 47 void Init( float ix=0, float iy=0, float iz=0, float iw=0 ) { SetX( ix ); SetY( iy ); SetZ( iz ); SetZ( iw ); } SetW( iw );
  • 48. 48 if (access & FILE_WRITE_ATTRIBUTES) output.append("tFILE_WRITE_ATTRIBUTESn"); if (access & FILE_WRITE_DATA) output.append("tFILE_WRITE_DATAn"); if (access & FILE_WRITE_EA) output.append("tFILE_WRITE_EAn"); if (access & FILE_WRITE_EA) output.append("tFILE_WRITE_EAn"); Same blocks
  • 49. 49 if ( protocol.EqualsIgnoreCase("http") || protocol.EqualsIgnoreCase("https") || protocol.EqualsIgnoreCase("news") || protocol.EqualsIgnoreCase("ftp") || protocol.EqualsIgnoreCase("file") || protocol.EqualsIgnoreCase("javascript") || protocol.EqualsIgnoreCase("ftp") ) { Double checking
  • 50. 50 Thou shalt not copy-paste thy code blocks
  • 51. 51
  • 53. 53 struct Foo { int a, b; }; bool operator==(Foo lhs, Foo rhs) { return lhs.a == rhs.a && lhs.b == rhs.b; }
  • 54. 54 struct Foo { int a, b; }; bool operator!=(Foo lhs, Foo rhs) { return !(lhs == rhs); } bool operator==(Foo lhs, Foo rhs) { return lhs.a == rhs.a && lhs.b == rhs.b; } So far so good
  • 55. 55 bool operator<(Foo lhs, Foo rhs) { ??? } bool operator<=(Foo lhs, Foo rhs) { ??? } bool operator>(Foo lhs, Foo rhs) { ??? } bool operator>=(Foo lhs, Foo rhs) { ??? } How about these?
  • 56. 56 bool operator<(Foo lhs, Foo rhs) { return lhs.a < rhs.a && lhs.b < rhs.b; } So far so good
  • 57. 57 bool operator<(Foo lhs, Foo rhs) { return lhs.a < rhs.a && lhs.b < rhs.b; } Foo { 2, 1 } < Foo { 1, 2 } Foo { 1, 2 } < Foo { 2, 1 } false false
  • 58. 58 bool operator<(Foo lhs, Foo rhs) { if (lhs.a < rhs.a) return true; if (rhs.a < lhs.a) return false; return lhs.b < rhs.b; } Foo { 2, 1 } < Foo { 1, 2 } Foo { 1, 2 } < Foo { 2, 1 } false true
  • 59. 59 struct Foo { double a; }; bool operator<(Foo lhs, Foo rhs) { return lhs.a < rhs.a; } bool operator>=(Foo lhs, Foo rhs) { return !(lhs < rhs); }
  • 60. 60 Foo { 1.0 } < Foo { 2.0 } Foo { 1.0 } < Foo { NaN } true false bool operator<(Foo lhs, Foo rhs) { return lhs.a < rhs.a; } bool operator>=(Foo lhs, Foo rhs) { return !(lhs < rhs); } Foo { 1.0 } >= Foo { NaN } true
  • 61. 61 So, what shall we do? struct Foo { // anything auto operator<=>(const Foo &rhs) const = default; };
  • 62. 62 Foo { 1.0 } < Foo { 2.0 } Foo { 1.0 } < Foo { NaN } true false struct Foo { // anything auto operator<=>(const Foo &rhs) const = default; }; Foo { 1.0 } >= Foo { NaN } false Foo { 2, 1 } < Foo { 1, 2 } Foo { 1, 2 } < Foo { 2, 1 } false true
  • 64. 64 const Ptree* pIf = IsA(p, ntIfStatement, ntSwitchStatement) ? p : IsA(First(p), ntIfStatement, ntSwitchStatement) && ContainsNoReturnStatements(First(p)) ? First(p) : nullptr;
  • 65. 65 const Ptree* pIf = IsA(p, ntIfStatement, ntSwitchStatement) ? p : IsA(First(p), ntIfStatement, ntSwitchStatement) && ContainsNoReturnStatements(First(p)) ? First(p) : nullptr;
  • 66. 66 Thy comparison routines shall be correct or else the Wrath of Code will get thee
  • 68. 68 struct G584_Info { G584_Info(/*A bunch of params*/) {/**/} const Ptree *m_p; bool m_add, m_mul; bool m_sub, m_div; };
  • 69. 69 auto what = p->What(); if (what == ntParenExpr) { // infs is std::vector<G584_Info>& infs.push_back( G584_Info(p, true, true, false, false) ); p = SafeSkipParentesis(p); } Possible copy
  • 70. 70 auto what = p->What(); if (what == ntParenExpr) { // infs is std::vector<G584_Info>& infs.emplace_back( p, true, true, false, false ); p = SafeSkipParentesis(p); } Better
  • 71. 71 struct G584_Info { G584_Info(/*A bunch of params*/) {/**/} const Ptree *m_p; bool m_add, m_mul; bool m_sub, m_div; }; emplace_back this? yes, since C++20
  • 72. 72 Thou shalt not push that which can be emplaced
  • 74. 74 auto&& infoMap = GetFunctionDangerousInfoMap(); auto it = infoMap.find(funcInfo); if (it == infoMap.end()) { infoMap.insert( std::make_pair(funcInfo, dangerousInfo)); } else { auto&& a = it->second; } Here we go again
  • 75. 75 auto it = infoMap.find(funcInfo); if (it == infoMap.end()) { infoMap.emplace(funcInfo, dangerousInfo); } else { auto&& a = it->second; } Better?
  • 76. 76 auto it = infoMap.find(funcInfo); if (it == infoMap.end()) { infoMap.emplace(funcInfo, dangerousInfo); } else { auto&& a = it->second; } Double lookup
  • 77. 77 if (auto [it, success] = infoMap.try_emplace(funcInfo, dangerousInfo); !success) { auto&& a = it->second; } Look, I fixed it
  • 78. 78 Thou shalt search only once
  • 79. 79
  • 81. 81 gcc is <ILLEGIBLE> broken As I understand it, those <BEEP>ing <BAD PEOPLE> decided to <ILLEGIBLE> break everything again. It worked before and now it's broken. For example, dropping the sign (a & 0x7fffffff) doesn't <BLEEP>ing work, and nothing works no more. They always <FLIP>ing break everything, those <CENSORED>s. Take your <WEEP>ing UB and <...>
  • 82. 82 int foo(const char *s) { int r = 0; while (*s) { r += ((r * 20891 + *s * 200) | *s ^ 4 | *s ^ 3 ) ^ (r >> 1); s++; } return r & 0x7fffffff; } Signed Overflow Drop the sign
  • 83. 83 foo(char const*): movzx edx, BYTE PTR [rdi] test dl, dl je .L4 xor esi, esi .L3: ; lots of calculations jne .L3 mov eax, esi and eax, 2147483647 ret .L4: xor eax, eax ret Drop the sign
  • 84. 84 int foo(const char *s) { int r = 0; while (*s) { r += ((r * 20891 + *s * 200) | *s ^ 4 | *s ^ 3 ) ^ (r >> 1); s++; } return r & 0x7fffffff; } gcc -O2 -std=c++17 -funsigned-char
  • 85. 85 foo(char const*): movzx edx, BYTE PTR [rdi] xor r8d, r8d test dl, dl je .L1 .L3: ; lots of calculations jne .L3 .L1: mov eax, r8d ret Oops
  • 86. 86 Thou shalt not cook signed values with overflow semantics
  • 88. 88 void func() noexcept { // .... throw SomeException{}; } This is REALLY bad
  • 89. 89 void func() noexcept { anotherFunc(); } This is also REALLY bad void anotherFunc() { throw SomeException{}; }
  • 90. 90 Not noexcept, but implies so DllMain
  • 91. 91 BOOL WINAPI DllMain(/**/) { BOOL br = TRUE; // .... if (FAILED(DllRegisterServer())) br = FALSE; // .... return br; }
  • 92. 92 BOOL WINAPI DllMain(/**/) { BOOL br = TRUE; // .... if (FAILED(DllRegisterServer())) br = FALSE; // .... return br; } PVS-Studio: don't throw from DllMain, mate
  • 93. 93 BOOL WINAPI DllMain(/**/) { BOOL br = TRUE; // .... if (FAILED(DllRegisterServer())) br = FALSE; // .... return br; } PVS-Studio: don't throw from DllMain, mate Me: LOLWUT?
  • 94. 94 • Part of Window API • Essentially, written in C • Related to COM • Doesn't throw DllRegisterServer Looks buggy Or does it?
  • 95. 95 HRESULT WINAPI DllRegisterServer(VOID) { // .... hr = ::RegOpenKeyEx(/**/); // .... DllGetObjectInfo(/**/); // .... hr = ::RegSetValueEx(/**/); // .... RegCloseKey(hk); } These don't throw
  • 96. 96 HRESULT WINAPI DllGetObjectInfo(/**/) { // .... hr = DllGetObject(/**/); if (SUCCEEDED(hr)) { // .... delete pPlugin; } // .... }
  • 97. 97 HRESULT WINAPI DllGetObject( DWORD dwPluginId, IShellPlugin **ppPlugin) { // .... *ppPlugin = new CCommandPlugin; // .... }
  • 98. 98 He who is without noexcept shall throw, and none other