price of an error

32
Price of an Error Andrey Karpov OOO "Program Verification Systems" (Co Ltd) [email protected]

Upload: andrey-karpov

Post on 14-Jul-2015

475 views

Category:

Software


1 download

TRANSCRIPT

Page 1: Price of an Error

Price of an Error

Andrey Karpov

OOO "Program Verification Systems" (Co Ltd)

[email protected]

Page 2: Price of an Error

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

Page 3: Price of an Error

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

Page 4: Price of an Error

How much does it cost to write a single line of code?

www.viva64.com

Page 5: Price of an Error

How much does it cost to write a single line of code?

28 $

www.viva64.com

Page 6: Price of an Error

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

Page 7: Price of an Error

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

Page 8: Price of an Error

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

Page 9: Price of an Error

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

Page 10: Price of an Error

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

Page 11: Price of an Error

How do developers view themselves

Ho do I see developers

Page 12: Price of an Error

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

Page 13: Price of an Error

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

Page 14: Price of an Error

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

Page 15: Price of an Error

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

Page 16: Price of an Error

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

Page 17: Price of an Error

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

Page 18: Price of an Error

“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

Page 19: Price of an Error

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

Page 20: Price of an Error

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

Page 21: Price of an Error

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

Page 22: Price of an Error

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

Page 23: Price of an Error

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

Page 24: Price of an Error

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

Page 25: Price of an Error

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

Page 26: Price of an Error

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

Page 27: Price of an Error

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

Page 28: Price of an Error

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

Page 29: Price of an Error

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);

Page 30: Price of an Error

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

Page 31: Price of an Error

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.

Page 32: Price of an Error

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