Download - Price of an Error
Price of an Error
Andrey Karpov
OOO "Program Verification Systems" (Co Ltd)
Introducing the speaker
• Andrey Nicolaevich Karpov, 1981
• scientific adviser in OOO "Program Verification Systems" (Co Ltd)
• candidate of science (philosophy doctor)
• MVP in Visual C++
• Intel Black Belt Software Developer
• one of the PVS-Studio (C/C++ source code analyzer) founders.
www.viva64.com
It is quite often that software developers have absolutely no clue about the cost of an error
• I have no reliable statistics on an average cost of an error, as they are too diverse and can be discovered at different stages of production.
• But, I know an average industry cost of a single line of source code. Try guessing it now.
www.viva64.com
Why is it so high?
• Large-scale projects usually do not possess a huge number of newly written code.
• Overheads are usually not taken into account.
www.viva64.com
What developers often forget:
• A software developer does not spend 100% of his working time on coding;
• Taxes and bonuses;
• Vacations and incapacity due to illness;
• Maintenance (accountancy, administration, janitor);
• Workspace environment, hardware, internet charges, rent, candies, paper for printer;
• Software;
• Etc.
www.viva64.com
What am I getting at?
• The price for an error is much higher than the programmer thinks it is.
• If an employee wastes an hour because of an error, this hour will not be wasted by him alone.
• This hour of unproductivity should be multiplied by a quite large factor.
• If a bug gets its way to the QA guy, it is just as terrible as it can get.
www.viva64.com
It is very important that the error be found at the earliest possible stage
• The earlier an error is found, the lower will be a price for fixing it.
• And here are some methods for an early detection:• Testing Driven Development;
• Static code analysis;
• Defensive programming.
• All of these methods are complementary.
www.viva64.com
We will discuss static code analysis and how you can protect yourself against several types of errors
• I cannot mention all possible issues, but it will give you an excuse for self-perfection while writing your own code.
• This experience is based upon checking over 200 open-source projects written on C and C++. The errors themselves are available here: http://www.viva64.com/en/examples/
www.viva64.com
How do developers view themselves
Ho do I see developers
Last line effect
• Similar to mountain-climbers;
• This statistics was generated from our library when it contained about 1500 examples of errors.
• I’ve detected 84 corresponding fragments.
• In 43 cases, the error was on the last line of code.
TrinityCore
inline Vector3int32& operator+=(const Vector3int32& other) {
x += other.x;
y += other.y;
z += other.y;
return *this;
}
www.viva64.com
Last Line Effect
Source Engine SDK
inline void Init(
float ix=0,
float iy=0,
float iz=0,
float iw = 0 )
{
SetX( ix );
SetY( iy );
SetZ( iz );
SetZ( iw );
}
Chromium
if (access & FILE_WRITE_ATTRIBUTES)
output.append(ASCIIToUTF16("\tFILE_WRITE_ATTRIBUTES\n"));
if (access & FILE_WRITE_DATA)
output.append(ASCIIToUTF16("\tFILE_WRITE_DATA\n"));
if (access & FILE_WRITE_EA)
output.append(ASCIIToUTF16("\tFILE_WRITE_EA\n"));
if (access & FILE_WRITE_EA)
output.append(ASCIIToUTF16("\tFILE_WRITE_EA\n"));
break;
www.viva64.com
Last Line Effect
qreal x = ctx->callData->args[0].toNumber(); Qt
qreal y = ctx->callData->args[1].toNumber();
qreal w = ctx->callData->args[2].toNumber();
qreal h = ctx->callData->args[3].toNumber();
if (!qIsFinite(x) || !qIsFinite(y) ||
!qIsFinite(w) || !qIsFinite(w))
if (!strncmp(vstart, "ASCII", 5)) OpenSSL
arg->format = ASN1_GEN_FORMAT_ASCII;
else if (!strncmp(vstart, "UTF8", 4))
arg->format = ASN1_GEN_FORMAT_UTF8;
else if (!strncmp(vstart, "HEX", 3))
arg->format = ASN1_GEN_FORMAT_HEX;
else if (!strncmp(vstart, "BITLIST", 3))
arg->format = ASN1_GEN_FORMAT_BITLIST;
www.viva64.com
Last Line Effect
1. The most simple one. One sholdknow about this effect and always check for it.
2. Impossible in real life. Use fewer Copy-Pastes.
3. Always align similar block of code, so that the error becomes immediately noticeable.
4. Use “tables”.0
10
20
30
40
50
1 2 3 4 5
www.viva64.com
Cases when table-style formatting can be utilized
Asterisk
static char
*token_equivs1[] =
{
....
"KW_IF",
"KW_IGNOREPAT",
"KW_INCLUDES"
"KW_JUMP",
"KW_MACRO",
"KW_PATTERN",
....
};
www.viva64.com
Cases when table-style formatting can be utilized
static char
*token_equivs1[] =
{
....
"KW_IF" ,
"KW_IGNOREPAT" ,
"KW_INCLUDES"
"KW_JUMP" ,
"KW_MACRO" ,
"KW_PATTERN" ,
....
};
Asterisk
static char
*token_equivs1[] =
{
....
"KW_IF",
"KW_IGNOREPAT",
"KW_INCLUDES"
"KW_JUMP",
"KW_MACRO",
"KW_PATTERN",
....
};
static char
*token_equivs1[] =
{
....
, "KW_IF"
, "KW_IGNOREPAT"
, "KW_INCLUDES"
"KW_JUMP"
, "KW_MACRO"
, "KW_PATTERN"
....
};
www.viva64.com
“The fault lies with the compiler”
It is better to try understanding what is going on before applying workarounds.
Ffdshow
TprintPrefs::TprintPrefs(....)
{
memset(this, 0, sizeof(this)); // This doesn't seem to
// help after optimization.
dx = dy = 0;
isOSD = false;
xpos = ypos = 0;
align = 0;
linespacing = 0;
sizeDx = 0;
sizeDy = 0;
...
}
www.viva64.com
Constructions protecting from mistakes.CountOf(). This is bad.
Shareaza
#define countof(array) (sizeof(array) / sizeof((array)[0]))
size_t AnsiDecodeChar(..., TCHAR arrChar[2], ....)
{
....
nCharSize = MultiByteToWideChar(....,
arrChar, countof(arrChar));
....
}
www.viva64.com
Constructions protecting from mistakes.CountOf(). This one is good.
Chromium
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
See more details in “PVS-Studio vs Chromium” article:
http://www.viva64.com/en/a/0074/
www.viva64.com
Constructions protecting from mistakes.CountOf(). This one is good.
Chromium
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
void Test(int C[3])
{
int A[3];
int *B = Foo();
size_t x = arraysize(A); // Ok
x = arraysize(B); // Compilation error
x = arraysize(C); // Compilation error
}www.viva64.com
Constructions protecting from mistakes.Automatic calculation of a size.
errno_t _strupr_s(char * _Str, size_t _Size);
template <size_t _Size> errno_t _strupr_s(char (&_String)[_Size]);
Implementation:
template <size_t _Size>
inline errno_t _strupr_s(_DstType (&_Dst)[_Size])
{
return _strupr_s(_Dst, _Size);
}
www.viva64.com
Do not be greedy when placing additional parenthesis and lines
Wine
if ((hr = SafeArrayGetUBound( sa, 1, &size ) != S_OK))
{
SafeArrayUnaccessData( sa );
return hr;
}
MongoDB
ss << (sizeof(char *) == 8) ? " 64bit" : " 32bit";
// A beautiful example.
// 0 or 1 will be printed instead of "32bit" / "64bit".
www.viva64.com
Do not be greedy when placing additional parenthesis and lines
Qt
bool QConfFileSettingsPrivate::readIniLine(....)
{
....
char ch;
while (i < dataLen &&
((ch = data.at(i) != '\n') && ch != '\r'))
++i;
....
}
www.viva64.com
Use static code analyzer
• Example from PVS-Studio: “an error which could not be discovered in 50 hours, was found by a single analyzer launch and fixed in less then an hour!”(Source: http://www.viva64.com/en/b/0221/)
• You can discover problems that you had no idea even existed.
www.viva64.com
Errors that are not known to even exist: char c = memcmp().
Such an error was a source of a serious vulnerability in MySQL/MariaDB up to verisions 5.1.61, 5.2.11, 5.3.5, 5.5.22. The essence here is that when a user connects to MySQL /MariaDB , a token is generated (SHA from password and hash), which in turn is compared to the expected result of ‘memcmp’ function. On several platforms, the return value can fall from the range of [-128..127]. As a result, in 1 chance in 235, the comparison of hash and the expected value always returns ‘true’, regardless of the hash itself. This leads to situation in which a simple bash command provides root access to a vulnerable MySQL server, even if the password is unknown.
typedef char my_bool;
...
my_bool check(...) {
return memcmp(...);
}
Details: Security vulnerability in MySQL/MariaD - http://seclists.org/oss-sec/2012/q2/493www.viva64.com
Errors that are not known to even exist: memset + optimization.
Overwriting memory - why? - http://www.viva64.com/en/k/0041/
V597. The compiler could delete the 'memset' function call, which is used to flush 'Foo' buffer. The RtlSecureZeroMemory() function should be used to erase the private data -http://www.viva64.com/en/d/0208/
php
char* php_md5_crypt_r(const char *pw,const char *salt, char *out)
{
static char passwd[MD5_HASH_MAX_LEN], *p;
unsigned char final[16];
....
/* Don't leave anything around in vm they could use. */
memset(final, 0, sizeof(final));
return (passwd);
}
www.viva64.com
Errors that are not known to even exist: strncat.
char *strncat(
char *strDest,
const char *strSource,
size_t count
);
MSDN: strncat does not check for
sufficient space in strDest; it
is therefore a potential cause
of buffer overruns. Keep in mind
that count limits the number of
characters appended; it is not a
limit on the size of strDest.
www.viva64.com
Errors that are not known to even exist: strncat.
char newProtoFilter[2048] = "....";
strncat(newProtoFilter, szTemp, 2048);
strncat(newProtoFilter, "|", 2048);
char filename[NNN];
...
strncat(filename,
dcc->file_info.filename,
sizeof(filename) - strlen(filename));
www.viva64.com
strncat(...., sizeof(filename) - strlen(filename) - 1);
Additional defense. Testing the tests.
Chromium: profile_sync_service_password_unittest.cc 261
static bool PasswordFormComparator(const PasswordForm& pf1,
const PasswordForm& pf2) {
......
if (pf1.username_value < pf2.username_value)
return true;
if (pf1.username_value < pf2.username_value)
return true;
if (pf1.password_element < pf2.password_element)
return true;
if (pf1.password_value < pf2.password_value)
return true;
return false;
}
It is better to be safe than sorry. On method compliments the other.www.viva64.com
Summary:
• A developer spends more time reading the code than writing it.
• Try writing as clear and simple as possible.
• Do not save on parenthesis, commas, spaces and lines.
• Format your code.
• Use static analysis.
• Employ “defensive programming”.
• Perform code reviews.
Time for questions.
E-Mail: [email protected]
My twitter: https://twitter.com/Code_Analysis
PVS-Studio: http://www.viva64.com/en/pvs-studio/
www.viva64.com