Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

download Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

of 15

Transcript of Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    1/15

    Let the world tremble! We've released

    PVS-Studio 4.00 with a free general-

    purpose analyzer!

    Author: Andrey Karpov

    Date: 01.12.2010

    Programmers, meet a new tool to search for errors in source code of software written in C/C++. Within

    the scope of the PVS-Studio analyzer, we implemented a new set of general-purpose rules. This

    functionality is free for now.

    You may download PVS-Studio here http://www.viva64.com/en/pvs-studio-download/.

    The article briefly describes new features of PVS-Studio and demonstrates the usage of new diagnostic

    capabilities with the example of static analysis of the TortoiseSVN project's source code.

    PVS-Studio is a state-of-the-art source code analyzer integrated into Microsoft Visual Studio

    2005/2008/2010 environment. The analyzer lets you conveniently handle the warning list and enables

    multi-core processing for analysis. PVS-Studio is meant for developers of modern Windows-software in

    C/C++/C++0x.

    Until now, PVS-Studio included two rule sets. The first was intended for detecting issues in 64-bit

    software while the second was intended for detecting issues in parallel software based on the OpenMP

    technology.

    Now our analyzer has a third universal set of rules that allows detecting various errors in code. This ruleset is free and may be used without any restrictions. We cannot say whether this set will become paid or

    not in future since we are only starting our way in the sphere of general-purpose static analysis.

    Currently you may study the new set of rules by downloading PVS-Studio 4.00 BETA. We will be glad to

    receive feedback from you concerning bugs and your wishes of how we could improve the tool. I would

    like to note it right away that we implemented only 50 general-purpose rules for a start. It is not too

    much, so if you fail to find any interesting issues in your code after you have downloaded and tried PVS-

    Studio, please do not jump to conclusions. We suggest that you try PVS-Studio in future when the set of

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    2/15

    diagnoses is greatly e

    tended. We intend to significantlyenlarge the base of diagnostic rulessoon (if we

    haveenough health and luc

    ).

    Let us de onstrate the use of PVS-Studio's new ruleset by thee

    ample ofTortoiseSV . TortoiseSV is

    a client for theSubversion version control system implemented as a Windows plugin. TortoiseSV is

    well known by many developers so I think there is no need to describe this application in detail. I only

    want to note that TortoiseSV was acknowledged as the best project in thecategory "tools and utilities

    for developers" in year 2007 on SourceForge.net.

    St p 1

    Download PVS-Studio from OOO "Program Verification Systems" company'ssite (that's us). I hopeyou

    will appreciate that you do not have to fill in any forms or solvecaptchas. Simplydownload the tool.

    St p 2

    Install PVS-Studio. Do not hesitate to click the "Ne

    t" button becauseyou do not have toset anything.

    The PVS-Studio package issigned with a digital signature. However, some antiviruses might get alerted

    about integration of PVS-Studio into Visual Studio. So you should allow any activity if asked byyour

    antivirus.

    St p 3

    Download the package ofsourcecode of the TortoiseSVN project. Weemployed version 1.6.11 of

    sourcecode.

    St p 4

    Open the TortoiseSVN project and launch the analysis bychoosing the Check Solution command in the

    PVS-Studio menu.

    St p 5

    The analy

    er will think a bit (the TortoiseSVN project is rather comple

    and includes a lot of files), so do

    not perform any actions andjust wait for a while. The progress dialogue will appear soon and the

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    3/15

    analysis will start. The analysis' speed depends upon the number of processor cores in your computer. If

    PVS-Studio consumes too many resources, you may restrain its appetite in thesettings.

    The analy

    er generates messages in its own window that hascontrols for enabling/disabling different

    types of messages. We will use thesecapabilities because we are not interested in a large number of

    errors related to 64 bits now. Besides, the64-bit analysis module ischarged and therefore isshipped in

    the trial mode (for more detailed information about the trial mode, go here).

    In the window, you maysee a group of three buttons responsible for displaying messages of the three

    rulesets.

    1)64 is responsible for displaying diagnostic messages about 64-bit issues (Viva64);

    2) MP shows diagnostic messages about parallel defects (VivaMP);

    3) GA shows the General Analysis diagnostic messages.

    Now we are interested only in the general analysis ruleset. Uncheck the other buttons and the

    unnecessary messages will be also hidden in the list.

    Wait for the analysis to finish.

    St p 6

    Analysis is over and we maysee the list of all the fragments in the program wherecode review is

    necessary.

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    4/15

    All the warnings are arranged according to 3 priority levels (this is a new feature in PVS-Studio 4.00 .

    Usually you have to review only messages of the 1-st and 2-nd levels. PVS-Studio 4.00 BETA generated

    33 warnings of the 1-st level, 14 warnings of the 2-nd level and 8 warnings of the 3-rd level.

    You'd better start examining the messages with the first level warnings. So you may uncheck the button

    responsible for displaying messages of the second level. The third level is disabled by default.

    Step 7

    Let's examine interesting code fragments the analyzer has found.

    Case 1

    In the beginning, there are two messages at once that refer to the same function. I hope that this

    function is not used too often in the code.

    V530 The return value of function 'empty' is required to be utilized. contextmenu.cpp 434

    V530 The return value of function 'remove' is required to be utilized. contextmenu.cpp 442

    STDMETHODIMP CShellExt::Initialize(....)

    {

    ...

    ignoredprops.empty();

    for (int p=0; p

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    5/15

    }

    Here and further we will give only brief comments on code fragments. To learn more why these code

    fragments are considered unsafe, refer to PVS-Studio's online-documentation (in Russian or English .

    The PVS-Studio distribution package also includes documentation in the pdf format (it is absolutely the

    same as the online-documentation . Further we will give links to the corresponding descriptions of the

    diagnostic messages.

    The V530 message warns us that "ignoredprops.empty( " does not clear the string at all while

    "std::remove( " will never remove the characters.

    Case 2

    Here it is checked whether a variable of the 'char' type is above or equal to value 0x80.

    V547 Expression 'c >= 0x80' is always false. The value range of signed char type: [-128, 127].

    pathutils.cpp 559

    CString CPathUtils::PathUnescape (const char* path)

    {

    // try quick path

    size_t i = 0;

    for (; char c = path[i]; ++i)

    if ((c >= 0x80) || (c == '%'))

    {

    // quick path does not work for

    // non-latin or escaped chars

    std::string utf8Path (path);

    CPathUtils::Unescape (&utf8Path[0]);

    return CUnicodeUtils::UTF8ToUTF16 (utf8Path);

    }

    ...

    }

    The V547 message tells you that such a check is meaningless. A 'char'-value is always below 0x80, so the

    condition above is always false. Perhaps it is because of this error that developers left the comment

    "quick path does not work for non-latin or escaped chars". Surely it does not, but not because of the

    code failing to convert the string: when it encounters a non-latin character, we simply cannot get inside

    the 'if' operator's body.

    Case 3

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    6/15

    Many threads are spawned and terminated by functions CreateThread/ExitThread. Therefore we risk

    quickly overflowing a thread's stack or failing to release some resources when a thread is terminated.

    For more information, refer to the V513 warning's description. It is much safer to use functions

    _beginthreadex( and _endthreadex( for these purposes.

    There is no need to give the code sample here, and the text of all the messages is the same:

    V513 Use _beginthreadex/_endthreadex functions instead of CreateThread/ExitThread functions.crashhandler.cpp 379

    Case 4

    It refers to TortoiseSVN companion utilities. It is highly probable that when you handle CrashLog, you

    will encounter another Crash.

    V510 The 'printf_s' function is not expected to receive class-type variable as fourth actual argument.

    excprpt.cpp 199

    string CExceptionReport::getCrashLog()

    {

    ...

    _tprintf_s(buf, _T("%s \\%s.xml"),

    getenv("TEMP"), CUtility::getAppName());

    ...

    }

    The V510 message warns you that it is a bad thing to pass an argument of the std::string type into the

    printf_s function. But it is std::string that the CUtility::getAppName( function returns. The error here is

    that the programmer forgot to write ".c_str( ". This may result either in an incorrect data output or

    program crash.

    Case 5

    The developers intended to clear an array here but failed.

    V530 The return value of function 'empty' is required to be utilized. mailmsg.cpp 40

    CMailMsg& CMailMsg::SetFrom(string sAddress,

    string sName)

    {

    if (initIfNeeded())

    {

    // only one sender allowed

    if (m_from.size())

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    7/15

    m_from.empty();

    m_from.push_back(TStrStrPair(sAddress,sName));

    }

    return *this;

    }

    Again, the V530 message tells us that it is "empty( " which is accidentally written instead of "clear( ".

    Case 6

    In the SetTo( function, the developers also failed to clear an array.

    V530 The return value of function 'empty' is required to be utilized. mailmsg.cpp 54

    CMailMsg& CMailMsg::SetTo(string sAddress,

    string sName)

    {

    if (initIfNeeded())

    {

    // only one recipient allowed

    if (m_to.size())

    m_to.empty();

    m_to.push_back(TStrStrPair(sAddress,sName));

    }

    return *this;

    }

    Case 7

    Of course the analyzer generates false alarms as well. For instance, this code fragment from the zlib

    library is included into the TortoiseSVN project. There is no error here but it is rather helpful to mark

    such a fragment with the V501 warning.

    V501 There are identical sub-expressions to the left and to the right of the '-' operator: size - size zutil.c

    213

    voidpf zcalloc (opaque, items, size)

    voidpf opaque;

    unsigned items;

    unsigned size;

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    8/15

    {

    /* make compiler happy */

    if (opaque) items += size - size;

    return (voidpf)calloc(items, size);

    }

    Of course the compiler feels happy here, but the subtraction operation looks suspicious.

    Case 8

    There are other grey areas concerning encodings. Here is one more condition which is always false.

    V547 Expression '* utf8CheckBuf >= 0xF5' is always false. The value range of signed char type: [-128,

    127]. tortoiseblame.cpp 312

    BOOL TortoiseBlame::OpenFile(const TCHAR *fileName)

    {

    ...

    // check each line for illegal utf8 sequences.

    // If one is found, we treat

    // the file as ASCII, otherwise we assume

    // an UTF8 file.

    char * utf8CheckBuf = lineptr;

    while ((bUTF8)&&(*utf8CheckBuf))

    {

    if ((*utf8CheckBuf == 0xC0)||

    (*utf8CheckBuf == 0xC1)||

    (*utf8CheckBuf >= 0xF5))

    {

    bUTF8 = false;

    break;

    }

    ...

    }

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    9/15

    By the way, conditions "*utf8CheckBuf == 0xC0" and "*utf8CheckBuf == 0xC1" are always false too. That

    is why the code "bUTF8 = false " will never get control. The fact that the PVS-Studio analyzer kept silent

    about the "*utf8CheckBuf == 0xC0" expression is its drawback. We have noted it and will make the

    analyzer to scold this issue in the next version.

    Case 9

    The next message is not so simple: in theory we have an error but in practice everything works well.

    V507 Pointer to local array 'stringbuf' is stored outside the scope of this array. Such a pointer will

    become invalid. mainwindow.cpp 277

    LRESULT CALLBACK CMainWindow::WinMsgHandler( ....)

    {

    ...

    if (pNMHDR->code == TTN_GETDISPINFO)

    {

    LPTOOLTIPTEXT lpttt;

    lpttt = (LPTOOLTIPTEXT) lParam;

    lpttt->hinst = hResource;

    // Specify the resource identifier of the

    // descriptive text for the given button.

    TCHAR stringbuf[MAX_PATH] = {0};

    ...

    lpttt->lpszText = stringbuf;

    }

    ...

    }

    The V507 message warns you that the object is being used after it has been destroyed. The 'stringbuf'

    buffer will be used after exiting the 'if' operator's body.

    If 'stringbuf' was a class object (for instance, of std::string class , the code would behave incorrectly. We

    would use an already destroyed object in that case. But here 'stringbuf' is an array created in the stack.

    The Visual C++ compiler does not use this stack fragment once again, so the buffer will continue to exist

    until the 'CMainWindow::WinMsgHandler' function terminates. Thus, there is no error yet this code is

    potentially dangerous.

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    10/15

    Case 10

    Here is one more fragment like the previous one. Again we have code that works but it is rather fragile.

    V507 Pointer to local array 'stringbuf' is stored outside the scope of this array. Such a pointer will

    become invalid. picwindow.cpp 443

    if ((HWND)wParam == m_AlphaSlid er.GetWindow())

    {

    LPTOOLTIPTEXT lpttt;

    lpttt = (LPTOOLTIPTEXT) lParam;

    lpttt->hinst = hResource;

    TCHAR stringbuf[MAX_PATH] = {0};

    _stprintf_s(stringbuf, .....);

    lpttt->lpszText = stringbuf;

    }

    Case 11

    It is a bad idea to throw exceptions and not to handle them in the destructor.

    V509 The 'throw' operator inside the destructor should be placed within the try..catch block. Raising

    exception inside the destructor is illegal. cachefileoutbuffer.cpp 52

    CCacheFileOutBuffer::~CCacheFileOutBuffer ()

    {

    if (IsOpen())

    {

    streamOffsets.push_back (GetFileSize());

    size_t lastOffset = streamOffsets[0];

    for (size_t i = 1,

    count = streamOffsets.size();

    i < count; ++i)

    {

    size_t offset = streamOffsets[i];

    size_t size = offset - lastOffset;

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    11/15

    if (size >= (DWORD)(-1))

    throw CStreamException("stream too large");

    Add ((DWORD)size);

    lastOffset = offset;

    }

    Add ((DWORD)(streamOffsets.size() -1));

    }

    }

    The V509 message warns you that if the CcacheFileOutBuffer object is destroyed while an exception is

    being handled, a new exception will cause a program crash.

    Case 12

    One more meaningless comparison.

    V547 Expression 'endRevision < 0' is always false. Unsigned type value is never < 0. cachelogquery.cpp

    999

    typedef index_t revision_t;

    ...

    void CCacheLogQuery::InternalLog (

    revision_t startRevision

    , revision_t endRevision

    , const CDictionaryBasedTempPath & startPath

    , int limit

    , const CLogOptions& options)

    {

    ...

    // we cannot receive logs for rev < 0

    if (endRevision < 0)

    endRevision = 0;

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    12/15

    ...

    }

    There simply cannot be any negative values here. The endRevision variable has the unsigned type and

    therefore endRevision is always above or equal to 0. The potential issue here is that if a negative value

    has been cast to unsigned type somewhere earlier, we will start handling a very large number. This

    cannot be checked.

    Case 13

    There are no more useful messages of the first level. Well, for now. It is only the first step in trying PVS-

    Studio's capabilities. We intend to develop not fewer than 150 issues we must teach our tool to detect. I

    would like to thank once again those readers who responded to our previous articles and sent us

    samples where one can detect errors with the help of static analysis at the stage of writing the code.

    Let's look at the second level. We found one interesting error related to using the Copy-Paste method.

    By the way, there was nothing interesting at the third level, so it is not for nothing that we disable it by

    default.

    V524 It is odd that the 'GetDbgHelpVersion' function is fully equivalent to the 'GetImageHlpVersion'

    function (SymbolEngine.h, line 98 . symbolengine.h 105

    BOOL GetImageHlpVersion(DWORD &dwMS, DWORD &dwLS)

    {

    return(GetInMemoryFileVersion(("DBGHELP.DLL"),

    dwMS,

    dwLS)) ;

    }

    BOOL GetDbgHelpVersion(DWORD &dwMS, DWORD &dwLS)

    {

    return(GetInMemoryFileVersion(("DBGHELP.DLL"),

    dwMS,

    dwLS)) ;

    }

    The V524 message is generated if the analyzer finds two suspiciously similar functions. It is most likely

    that the first function must get the "imagehlp.dll" version of the file instead of "dbghelp.dll ".

    Step 8

    Now we must fix the errors we have found. This step is clear and we will skip it.

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    13/15

    Concerning the errors found, we will report them to TortoiseSVN's developers.

    Step 9

    Now let's speak a bit about false alarms. Let me give you some examples to explain what false alarms

    are and how we can deal with them.

    Here is the first false alarm: PVS-Studio did not understand the playing with the operation of memory

    copying.

    V512 A call of the 'memcpy' function will lead to a buffer overflow or underflow. resmodule.cpp 838

    const WORD*

    CResModule::CountMemReplaceMenuExResource( ....)

    {

    ...

    if (newMenu != NULL) {

    CopyMemory(&newMenu[*wordcount], p0, 7 * sizeof(WORD));

    }

    ...

    }

    The V512 warning informs you that we have a buffer underflow or, vice versa, a buffer overflow. The

    analyzer made a mistake this time having suggested that we want to copy 7 objects but intend to handle

    only one object of the WORD type.

    Here is the second false alarm. The analyzer suggests that we processed only a part of the array.

    V512 A call of the 'memcmp' function will lead to a buffer overflow or underflow. sshsha.c 317

    static int sha1_96_verify(....)

    {

    unsigned char correct[20];

    sha1_do_hmac(handle, blk, len, seq, correct);

    return !memcmp(correct, blk + len, 12);

    }

    Right, it is only a part of the 'correct' array participating in the comparison operation but we made it

    intentionally.

    The third example of a false alarm.

    V517 The use of 'if (A {...} else if (A {...}' pattern was detected. There is a probability of logical error

    presence. tree234.c 195

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    14/15

    static void *add234_internal( ....)

    {

    ...

    if ((c = t->cmp(e, n->elems[0])) < 0)

    childnum = 0;

    else if (c == 0)

    return n->elems[0]; /* already exists */

    else if (n->elems[1] == NULL

    || (c = t->cmp(e, n->elems[1])) < 0)

    childnum = 1;

    else if (c == 0)

    return n->elems[1]; /* already exists */

    else if (n->elems[2] == NULL

    || (c = t->cmp(e, n->elems[2])) < 0)

    childnum = 2;

    else if (c == 0)

    return n->elems[2]; /* already exists */

    else

    childnum = 3;

    ...

    }

    The analyzer does not like that the check 'c == 0' is present in code several times. The code is correct

    since the 'c' variable is changed inside the other conditions "c = t->cmp(e, n->elems[2] ". But this

    situation is rather rare. Usually the V517 message points to real defects in code.

    We will not consider all the rest of false alarms because there is nothing interesting about them. A

    programmer can easily understand that they are false alarms and he does not have to examine themtooclosely.

    You may handle false alarms in several ways:

    1 You may rewrite the code. Sometimes it is rather reasonable. Refactoring would be very helpful for

    the last sample with a false alarm (I mean the add234_internal function and warning V517 .

  • 8/6/2019 Let the world tremble! We've released PVS-Studio 4.00 with a free general-purpose analyzer!

    15/15

    2 You may disable some diagnoses in the settings which always produce false alarms in your projects.

    After you disable them, all the corresponding messages will disappear from the warning list. For more

    details, see "Settings: Detectable Errors".

    3) If false alarms refer to code that does not need to be checked, you may exclude separate files or

    folders from analysis. You may also use masks. For details, see "Settings: Don't Check Files". This method

    is convenient for excluding third-party libraries from analysis.

    4) You may use the mechanism of suppressing messages containing particular text. For details, see

    "Settings: Message Suppression".

    5) There are cases when you should suppress a particular false alarm. Then you may use the "Mark as

    False Alarm" option. If you use it, the analyzer adds a small comment of the "//-Verror_code" kind into

    the code. You may hide code fragments marked with this comment by the FA button that enables or

    disables displaying the marked messages. For details, see: "False alarm suppression".

    Thank you for your attention. Try PVS-Studio. Send us your feedback. Ask us questions. Give us your

    interesting samples. Offer new diagnostic rules we could implement.

    Sincerely yours, Andrey Karpov, one of the PVS-Studio's developers.

    You may contact us on page "Feedback".

    Or by e-mail: support[@]viva64.com , karpov[@]viva64.com.

    Write to us, we have a wonderful support service. It is me, the author of this article, who will personally

    participate in communication unlike most companies where you deal with some abstract human-robot.