SlideShare a Scribd company logo
Checking VirtualDub 
Author: Andrey Karpov 
Date: 18.10.2013 
Just recently I've checked the VirtualDub project with PVS-Studio. This was a random choice. You see, I 
believe that it is very important to regularly check and re-check various projects to show users that the PVS-Studio 
analyzer is evolving, and which project you run it on doesn't matter that much - bugs can be found 
everywhere. We already checked the VirtualDub project in 2011, but we found almost nothing of interest 
then. So, I decided to take a look at it now, 2 years later. 
I downloaded the archive VirtualDub-1.10.3-src.7z from the VirtualDub website. Analysis was performed by 
PVS-Studio 5.10. It took me just about one hour, so don't be strict with me. I surely must have missed 
something or, on the contrary, taken correct code fragments for incorrect ones. If you develop and maintain 
the VirtualDub project, please don't rely on my report - check it yourselves. We always help the open-source 
community and will grant you a registration key. 
I'm also asking Avery Lee to get me right. Last time his reaction to my mentioning VirtualDub in one of the 
articles was pretty negative. I never mean to say about any program that it's buggy. Software errors can be 
found in every program. My goal is to show how useful the static code analysis technology can be. At the 
same time, it will help to make open-source projects a bit more reliable. And that's wonderful. 
Single-time checks are of little use, of course. But that I cannot help, I'm afraid. Whether or not to use static 
analysis tools on a regular basis is up to developers. I can only try to explain why regular use is better. Here's 
one interesting post on the subject: Leo Tolstoy and static code analysis. 
However, this article is about bugs, not the static analysis methodology. Let's find out if there is anything 
interesting PVS-Studio has found in VirtualDub. 
Virtual destructors 
In C++, the destructor of a polymorphic base class must be declared virtual - this is the only way to ensure 
correct destruction of a derived object through a pointer to the corresponding base class. 
I know you know that. However, it still doesn't guarantee that you will never forget to declare the 
destructor virtual.
There is the class VDDialogBaseW32 in VirtualDub: 
class VDDialogBaseW32 { 
.... 
~VDDialogBaseW32(); 
.... 
virtual INT_PTR DlgProc(....) = 0; 
virtual bool PreNCDestroy(); 
.... 
} 
As you can see, it contains virtual functions. The destructor, however, is not declared virtual. And, naturally, 
there are some classes inherited from it, for example VDDialogAudioFilterFormatConvConfig: 
class VDDialogAudioFilterFormatConvConfig : 
public VDDialogBaseW32 
{ .... }; 
Here's the object destruction error: 
INT_PTR CALLBACK VDDialogBaseW32::StaticDlgProc(....) { 
VDDialogBaseW32 *pThis = 
(VDDialogBaseW32 *)GetWindowLongPtr(hwnd, DWLP_USER); 
.... 
delete pThis; 
.... 
} 
PVS-Studio's diagnostic message: V599 The destructor was not declared as a virtual one, although the 
'VDDialogBaseW32' class contains virtual functions. VirtualDub gui.cpp 997 
As you can see, a pointer to the base class is used to destroy the object. Doing it this way will cause an 
undefined behavior. 
The same trouble is with the class VDMPEGAudioPolyphaseFilter.
A bit more about the undefined behavior 
It's all clear with bugs related to virtual destructors. Shift operations, however, is a more subtle subject. 
Take a look at the following example: 
void AVIVideoGIFOutputStream::write(....) { 
{ 
.... 
for(int i=0; i<palsize; ++i) 
dict[i].mPrevAndLastChar = (-1 << 16) + i; 
.... 
} 
However hard one may try to convince me that this is an absolutely safe code which has been working for a 
dozen years, I'll keep saying that we are still having an undefined behavior here. Let's see what the standard 
has to say about such constructs: 
The shift operators << and >> group left-to-right. 
shift-expression << additive-expression 
shift-expression >> additive-expression 
The operands shall be of integral or unscoped enumeration type and integral promotions are performed. 
1. The type of the result is that of the promoted left operand. The behavior is undefined if the right operand 
is negative, or greater than or equal to the length in bits of the promoted left operand. 
2. The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned 
type, the value of the result is E1 * 2^E2, reduced modulo one more than the maximum value representable 
in the result type. Otherwise, if E1 has a signed type and non-negative value, and E1*2^E2 is representable 
in the result type, then that is the resulting value; otherwise, the behavior is undefined. 
3. 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. 
That the code works correctly is sheer luck, and it may suddenly change its behavior once you have 
switched over to a new compiler or started using compiler switches for optimization. See the article "Wade 
not in unknown waters. Part three" for more information about shift operations and code fixing in such 
cases. 
Here's the list of fragments of VirtualDub where PVS-Studio has detected Undefined behavior or 
Unspecified behavior.
Misprints 
static ModuleInfo *CrashGetModules(void *&ptr) { 
.... 
while(*pszHeap++); 
if (pszHeap[-1]=='.') 
period = pszHeap-1; 
.... 
} 
PVS-Studio's diagnostic message: V529 Odd semicolon ';' after 'while' operator. VirtualDub crash.cpp 462 
Note the semicolon after 'while'. It's either a mistake or incorrect code formatting. It seems more like the 
first thing. The loop "while(*pszHeap++);" will reach the end of the line and result in the 'pszHeap' variable 
pointing to the memory area after the terminal null. The check "if (pszHeap[-1]=='.')" is meaningless: it's the 
terminal null which is always found at "pszHeap[-1]". 
Here's another misprint when handling strings. 
void VDBackfaceService::Execute(...., char *s) { 
.... 
if (*s == '"') { 
while(*s && *s != '"') 
++s; 
} else { 
.... 
} 
PVS-Studio's diagnostic message: V637 Two opposite conditions were encountered. The second condition is 
always false. Check lines: 183, 184. VirtualDub backface.cpp 183 
This code must skip everything enclosed in the quotes. At least, it seems to do so. However, the condition 
(*s && *s != '"') is false right away. Perhaps the code should look like this: 
if (*s == '"') { 
++s; 
while(*s && *s != '"') 
++s;
} 
The new operator throws exceptions when a memory allocation error 
occurs 
In old code you can often see checks of values returned by the new operator: 
int *p = new int[10]; 
if (!p) 
return false; 
Contemporary C++ compilers conforming to the C++ standard must throw an exception when memory 
cannot be allocated. You can set the 'new' operator not to do this, but this is outside the scope of our article 
now. 
Therefore, the check if (!p) is not necessary. This code is safe in general - just an odd check, that's all. 
But old code fragments may do you much harm, too. Take a look at the fragment from VirtualDub below. 
void HexEditor::Find(HWND hwndParent) { 
.... 
int *next = new int[nFindLength+1]; 
char *searchbuffer = new char[65536]; 
char *revstring = new char[nFindLength]; 
.... 
if (!next || !searchbuffer || !revstring) { 
delete[] next; 
delete[] searchbuffer; 
delete[] revstring; 
return; 
} 
.... 
} 
PVS-Studio's diagnostic message: V668 There is no sense in testing the 'next' pointer against null, as the 
memory was allocated using the 'new' operator. The exception will be generated in the case of memory 
allocation error. VirtualDub hexviewer.cpp 2012
If an exception is thrown in the line "char *revstring = new char[nFindLength];", a memory leak will occur. 
The delete[] operators won't be called. This is not a critical error, but it is worth mentioning. 
See the list of all the fragments of VirtualDub where a pointer is checked after calling the 'new' operator. 
A reference to a destroyed object 
vdlist_iterator& operator--(int) { 
vdlist_iterator tmp(*this); 
mp = mp->mListNodePrev; 
return tmp; 
} 
PVS-Studio's diagnostic message: V558 Function returns the reference to temporary local object: tmp. 
VirtualDub vdstl.h 460 
The function is implemented incorrectly: it returns a reference to the local object 'tmp'. After leaving the 
function, this object will have been destroyed already; handling that reference will cause an undefined 
behavior. 
By the way, the ++ operator, standing nearby, is implemented correctly. 
First use, then check 
In various programs you can often see a bug when a pointer is first dereferenced and only then is checked 
for being NULL. These errors may stay hidden for a very long time because a pointer being null is a rare 
accident. VirtualDub also has a few of these. For example: 
void VDTContextD3D9::Shutdown() { 
.... 
mpData->mFenceManager.Shutdown(); 
.... 
if (mpData) { 
if (mpData->mhmodD3D9) 
FreeLibrary(mpData->mhmodD3D9); 
.... 
}
PVS-Studio's diagnostic message: V595 The 'mpData' pointer was utilized before it was verified against 
nullptr. Check lines: 1422, 1429. Tessa context_d3d9.cpp 1422 
The pointer "mpData" gets first dereferenced and then checked: "if (mpData)". These errors usually occur 
during code refactoring: the new code is inserted before the necessary checks. 
The other fragments which triggered the V595 diagnostic are listed here. 
Handling the HRESULT type 
VDPosition AVIReadTunnelStream::TimeToPosition(VDTime timeInUs) { 
AVISTREAMINFO asi; 
if (AVIStreamInfo(pas, &asi, sizeof asi)) 
return 0; 
return VDRoundToInt64(timeInUs * (double)asi.dwRate / 
(double)asi.dwScale * (1.0 / 1000000.0)); 
} 
PVS-Studio's diagnostic message: V545 Such conditional expression of 'if' operator is incorrect for the 
HRESULT type value 'AVIStreamInfoA(pas, & asi, sizeof asi)'. The SUCCEEDED or FAILED macro should be 
used instead. VirtualDub avireadhandlertunnelw32.cpp 230 
The function AVIStreamInfo() returns a HRESULT value. This type cannot be interpreted as 'bool'. 
Information stored in a variable of the HRESULT type has a pretty complex structure, and to check a 
HRESULT value one needs to use either the SUCCEEDED or FAILED macros declared in "WinError.h". This is 
how they are implemented: 
#define FAILED(hr) (((HRESULT)(hr)) < 0) 
#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) 
The fixed code should look like this: 
if (FAILED(AVIStreamInfo(pas, &asi, sizeof asi))) 
The same warning is generated on the following lines: 
• avireadhandlertunnelw32.cpp 238 
• avireadhandlertunnelw32.cpp 335 
• inputfileavi.cpp 440 
• context_d3d11.cpp 959
Magic numbers 
It's not a good idea to declare a string length as a number. You may easily make a mistaken when counting 
the characters. For example: 
bool VDOpenGLBinding::Attach(....) { 
.... 
if (!memcmp(start, "GL_EXT_blend_subtract", 20)) 
.... 
} 
PVS-Studio's diagnostic message: V512 A call of the 'memcmp' function will lead to underflow of the buffer 
'"GL_EXT_blend_subtract"'. Riza opengl.cpp 393 
The length of the string "GL_EXT_blend_subtract" is 21 characters, not 20. This error is not critical; no 
troubles usually occur in practice. But you'd still better avoid such magic numbers and rather use a special 
macro to count the string length. For example: 
#define LiteralStrLen(S) (sizeof(S) / sizeof(S[0]) - 1) 
C++ allows you to create a template function which is safer: 
template <typename T, size_t N> 
char (&ArraySizeHelper(T (&array)[N]))[N]; 
template <typename T, size_t N> 
size_t LiteralStrLen(T (&array)[N]) { 
return sizeof(ArraySizeHelper(array)) - 1; 
} 
The advantage of the second method is that it doesn't allow you to accidentally pass a plain pointer as an 
argument. This method is described in detail in the article "PVS-Studio vs Chromium". 
Absolute paths 
VDDbgHelpDynamicLoaderW32::VDDbgHelpDynamicLoaderW32() 
{ 
hmodDbgHelp = LoadLibrary( 
"c:program filesdebugging tools for windowsdbghelp");
if (!hmodDbgHelp) { 
hmodDbgHelp = LoadLibrary("c:program files (x86)...... 
.... 
} 
PVS-Studio's diagnostic message: V631 Consider inspecting the 'LoadLibraryA' function call. Defining an 
absolute path to the file or directory is considered a poor style. VirtualDub leaks.cpp 67, 69 
I guess you understand what is bad about this code. It has to do with debugging of course and doesn't seem 
to affect the end users in any way, but it's still better to get a correct path to Program Files. 
An incorrect argument 
sint64 rva; 
void tool_lookup(....) { 
.... 
printf("%08I64x %s + %x [%s:%d]n", 
addr, sym->name, addr-sym->rva, fn, line); 
.... 
} 
PVS-Studio's diagnostic message: V576 Incorrect format. Consider checking the fourth actual argument of 
the 'printf' function. The argument is expected to be not greater than 32-bit. Asuka lookup.cpp 56 
The variable 'rva' is a 64-bit type, which means that it will write 8 bytes into the stack. The function printf() 
is a variadic function. The data type it must process is specified by the format string. In our case, the 'rva' 
variable will be processed as a 32-bit variable ("%x"). 
Whether or not this error will cause any faults depends on how in particular the compiler will be passing the 
arguments and on the platform bitness. For example, all the integer types in Win64 are first cast to a 64-bit 
type and only then are written into the stack, so there'll be no trouble with a variable occupying more stack 
memory than necessary. 
However, if the variable 'rva' stores values bigger than INT_MAX, its value will be incorrectly printed 
anyway. 
The same warning is generated for the following fragments: 
• dubstatus.cpp 360 
• lookup.cpp 58
Incorrect comparisons 
void VDVideoCompressorVCM::GetState(vdfastvector<uint8>& data) { 
DWORD res; 
.... 
res = ICGetState(hic, data.data(), size); 
.... 
if (res < 0) 
throw MyICError("Video compression", res); 
} 
PVS-Studio's diagnostic message: V547 Expression 'res < 0' is always false. Unsigned type value is never < 0. 
Riza w32videocodecpack.cpp 828 
The variable 'res' is unsigned DWORD. It means that the "res < 0" expression will always give 'false'. 
A similar check can be found in w32videocodec.cpp 284. 
Here's one more bug of that kind. 
#define ICERR_CUSTOM -400L 
static const char *GetVCMErrorString(uint32 icErr) { 
.... 
if (icErr <= ICERR_CUSTOM) err = "A codec-specific error occurred."; 
.... 
} 
PVS-Studio's diagnostic message: V605 Consider verifying the expression: icErr <= - 400L. An unsigned value 
is compared to the number -400. system error_win32.cpp 54 
The variable ' icErr' is 'unsigned', therefore the number '-400' will be implicitly cast to 'unsigned' before 
executing the comparison. As a result, the number '-400' will turn into 4294966896. Thus, the comparison 
(icErr <= -400) is equivalent to (icErr <= 4294966896). I guess this is far not what the programmer intended. 
Miscellaneous strange stuff 
void AVIOutputFile::finalize() {
.... 
if (stream.mChunkCount && hdr.dwScale && stream.mChunkCount) 
.... 
} 
PVS-Studio's diagnostic message: V501 There are identical sub-expressions 'stream.mChunkCount' to the 
left and to the right of the '&&' operator. VirtualDub avioutputfile.cpp 761 
The variable 'stream.mChunkCount' is checked twice. Either one of the checks is not necessary or something 
else should have been checked. 
void VDVideoCompressorVCM::Start(const void *inputFormat, 
uint32 inputFormatSize, 
const void *outputFormat, 
uint32 outputFormatSize, 
const VDFraction& frameRate, 
VDPosition frameCount) 
{ 
this->hic = hic; 
.... 
} 
PVS-Studio's diagnostic message: V570 The 'this->hic' variable is assigned to itself. Riza 
w32videocodecpack.cpp 253 
void VDDialogAudioConversionW32::RecomputeBandwidth() { 
.... 
if (IsDlgButtonChecked(mhdlg, IDC_PRECISION_NOCHANGE)) { 
if (mbSourcePrecisionKnown && mbSource16Bit) 
bps *= 2; 
else 
bps = 0;
} if (IsDlgButtonChecked(mhdlg, IDC_PRECISION_16BIT)) 
bps *= 2; 
.... 
} 
PVS-Studio's diagnostic message: V646 Consider inspecting the application's logic. It's possible that 'else' 
keyword is missing. VirtualDub optdlg.cpp 120 
Seems like incorrect code formatting. Or perhaps the keyword 'else' is missing. 
bool VDCaptureDriverScreen::Init(VDGUIHandle hParent) { 
.... 
mbAudioHardwarePresent = false; 
mbAudioHardwarePresent = true; 
.... 
} 
PVS-Studio's diagnostic message: V519 The 'mbAudioHardwarePresent' variable is assigned values twice 
successively. Perhaps this is a mistake. Check lines: 274, 275. VDCapture cap_screen.cpp 275 
Conclusion 
As you can see, even running static analysis for once may be very useful. But it is much more efficient to run 
it regularly. Programmers keep compiler warnings turned on all the time, not only once before the release, 
don't they? It's the same with static analysis tools. Using them regularly allows you to eliminate any bugs as 
soon as they occur. Think of PVS-Studio as kind of an additional story over the compiler which generates 
some more worthy warnings. It's best of all to use the incremental analysis: it allows you to detect bugs in 
freshly modified files right after compilation.

More Related Content

PDF
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
Andrey Karpov
 
PDF
A fresh eye on Oracle VM VirtualBox
PVS-Studio
 
PDF
Linux version of PVS-Studio couldn't help checking CodeLite
PVS-Studio
 
PDF
Analyzing the Blender project with PVS-Studio
PVS-Studio
 
PDF
Checking Clang 11 with PVS-Studio
Andrey Karpov
 
PDF
Checking the Cross-Platform Framework Cocos2d-x
Andrey Karpov
 
PDF
Re-checking the ReactOS project - a large report
PVS-Studio
 
PDF
Checking the code of Valgrind dynamic analyzer by a static analyzer
PVS-Studio
 
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
Andrey Karpov
 
A fresh eye on Oracle VM VirtualBox
PVS-Studio
 
Linux version of PVS-Studio couldn't help checking CodeLite
PVS-Studio
 
Analyzing the Blender project with PVS-Studio
PVS-Studio
 
Checking Clang 11 with PVS-Studio
Andrey Karpov
 
Checking the Cross-Platform Framework Cocos2d-x
Andrey Karpov
 
Re-checking the ReactOS project - a large report
PVS-Studio
 
Checking the code of Valgrind dynamic analyzer by a static analyzer
PVS-Studio
 

What's hot (20)

PDF
Date Processing Attracts Bugs or 77 Defects in Qt 6
Andrey Karpov
 
PDF
The Unicorn's Travel to the Microcosm
Andrey Karpov
 
PDF
Waiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
PVS-Studio
 
PDF
CppCat Static Analyzer Review
Andrey Karpov
 
PDF
Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
Andrey Karpov
 
PDF
PVS-Studio Meets Octave
PVS-Studio
 
PDF
Checking the Qt 5 Framework
Andrey Karpov
 
PDF
Intel IPP Samples for Windows - error correction
Andrey Karpov
 
PDF
Intel IPP Samples for Windows - error correction
PVS-Studio
 
PDF
Rechecking SharpDevelop: Any New Bugs?
PVS-Studio
 
PDF
Zero, one, two, Freddy's coming for you
Andrey Karpov
 
PDF
Documenting Bugs in Doxygen
PVS-Studio
 
PDF
PVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio
 
PDF
Dusting the globe: analysis of NASA World Wind project
PVS-Studio
 
PDF
Of complicacy of programming, or won't C# save us?
PVS-Studio
 
PDF
Checking Oracle VM VirtualBox. Part 1
Andrey Karpov
 
PDF
PVS-Studio delved into the FreeBSD kernel
PVS-Studio
 
PDF
Checking Bitcoin
Andrey Karpov
 
PDF
Tesseract. Recognizing Errors in Recognition Software
Andrey Karpov
 
PDF
A few words about OpenSSL
PVS-Studio
 
Date Processing Attracts Bugs or 77 Defects in Qt 6
Andrey Karpov
 
The Unicorn's Travel to the Microcosm
Andrey Karpov
 
Waiting for the Linux-version: Checking the Code of Inkscape Graphics Editor
PVS-Studio
 
CppCat Static Analyzer Review
Andrey Karpov
 
Miranda NG Project to Get the "Wild Pointers" Award (Part 1)
Andrey Karpov
 
PVS-Studio Meets Octave
PVS-Studio
 
Checking the Qt 5 Framework
Andrey Karpov
 
Intel IPP Samples for Windows - error correction
Andrey Karpov
 
Intel IPP Samples for Windows - error correction
PVS-Studio
 
Rechecking SharpDevelop: Any New Bugs?
PVS-Studio
 
Zero, one, two, Freddy's coming for you
Andrey Karpov
 
Documenting Bugs in Doxygen
PVS-Studio
 
PVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio
 
Dusting the globe: analysis of NASA World Wind project
PVS-Studio
 
Of complicacy of programming, or won't C# save us?
PVS-Studio
 
Checking Oracle VM VirtualBox. Part 1
Andrey Karpov
 
PVS-Studio delved into the FreeBSD kernel
PVS-Studio
 
Checking Bitcoin
Andrey Karpov
 
Tesseract. Recognizing Errors in Recognition Software
Andrey Karpov
 
A few words about OpenSSL
PVS-Studio
 
Ad

Similar to Checking VirtualDub (20)

PDF
LibRaw, Coverity SCAN, PVS-Studio
Andrey Karpov
 
PDF
Accord.Net: Looking for a Bug that Could Help Machines Conquer Humankind
PVS-Studio
 
PDF
Checking Notepad++: five years later
PVS-Studio
 
PDF
Python and Ruby implementations compared by the error density
PVS-Studio
 
PDF
Checking the Source Code of FlashDevelop with PVS-Studio
PVS-Studio
 
PDF
Errors detected in the Visual C++ 2012 libraries
PVS-Studio
 
PDF
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
PVS-Studio
 
PDF
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
Andrey Karpov
 
PDF
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
PVS-Studio
 
PDF
Top 10 C# projects errors found in 2016
PVS-Studio
 
PDF
A Spin-off: Firebird Checked by PVS-Studio
Andrey Karpov
 
PDF
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
PVS-Studio
 
PDF
Microsoft opened the source code of Xamarin.Forms. We couldn't miss a chance ...
PVS-Studio
 
PDF
Brief analysis of Media Portal 2 bugs
PVS-Studio
 
PDF
Picking Mushrooms after Cppcheck
Andrey Karpov
 
PPTX
PVS-Studio. Static code analyzer. Windows/Linux, C/C++/C#. 2017
Andrey Karpov
 
PDF
Checking PVS-Studio with Clang
Andrey Karpov
 
PDF
Top 10 bugs in C++ open source projects, checked in 2016
PVS-Studio
 
PDF
How to find 56 potential vulnerabilities in FreeBSD code in one evening
PVS-Studio
 
PDF
Analyzing the Dolphin-emu project
PVS-Studio
 
LibRaw, Coverity SCAN, PVS-Studio
Andrey Karpov
 
Accord.Net: Looking for a Bug that Could Help Machines Conquer Humankind
PVS-Studio
 
Checking Notepad++: five years later
PVS-Studio
 
Python and Ruby implementations compared by the error density
PVS-Studio
 
Checking the Source Code of FlashDevelop with PVS-Studio
PVS-Studio
 
Errors detected in the Visual C++ 2012 libraries
PVS-Studio
 
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
PVS-Studio
 
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
Andrey Karpov
 
A Unicorn Seeking Extraterrestrial Life: Analyzing SETI@home's Source Code
PVS-Studio
 
Top 10 C# projects errors found in 2016
PVS-Studio
 
A Spin-off: Firebird Checked by PVS-Studio
Andrey Karpov
 
Comparing the general static analysis in Visual Studio 2010 and PVS-Studio by...
PVS-Studio
 
Microsoft opened the source code of Xamarin.Forms. We couldn't miss a chance ...
PVS-Studio
 
Brief analysis of Media Portal 2 bugs
PVS-Studio
 
Picking Mushrooms after Cppcheck
Andrey Karpov
 
PVS-Studio. Static code analyzer. Windows/Linux, C/C++/C#. 2017
Andrey Karpov
 
Checking PVS-Studio with Clang
Andrey Karpov
 
Top 10 bugs in C++ open source projects, checked in 2016
PVS-Studio
 
How to find 56 potential vulnerabilities in FreeBSD code in one evening
PVS-Studio
 
Analyzing the Dolphin-emu project
PVS-Studio
 
Ad

More from Andrey Karpov (20)

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

Recently uploaded (20)

PPTX
Odoo Integration Services by Candidroot Solutions
CandidRoot Solutions Private Limited
 
PDF
lesson-2-rules-of-netiquette.pdf.bshhsjdj
jasmenrojas249
 
PDF
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
PDF
advancepresentationskillshdhdhhdhdhdhhfhf
jasmenrojas249
 
PDF
Download iTop VPN Free 6.1.0.5882 Crack Full Activated Pre Latest 2025
imang66g
 
PDF
Key Features to Look for in Arizona App Development Services
Net-Craft.com
 
PDF
Summary Of Odoo 18.1 to 18.4 : The Way For Odoo 19
CandidRoot Solutions Private Limited
 
PDF
An Experience-Based Look at AI Lead Generation Pricing, Features & B2B Results
Thomas albart
 
PDF
WatchTraderHub - Watch Dealer software with inventory management and multi-ch...
WatchDealer Pavel
 
PDF
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
PPTX
GALILEO CRS SYSTEM | GALILEO TRAVEL SOFTWARE
philipnathen82
 
PDF
Protecting the Digital World Cyber Securit
dnthakkar16
 
PPTX
Presentation about Database and Database Administrator
abhishekchauhan86963
 
PDF
Salesforce Implementation Services Provider.pdf
VALiNTRY360
 
PPTX
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
PDF
Generating Union types w/ Static Analysis
K. Matthew Dupree
 
PDF
49784907924775488180_LRN2959_Data_Pump_23ai.pdf
Abilash868456
 
PPT
Activate_Methodology_Summary presentatio
annapureddyn
 
PDF
Using licensed Data Loss Prevention (DLP) as a strategic proactive data secur...
Q-Advise
 
PPTX
The-Dawn-of-AI-Reshaping-Our-World.pptxx
parthbhanushali307
 
Odoo Integration Services by Candidroot Solutions
CandidRoot Solutions Private Limited
 
lesson-2-rules-of-netiquette.pdf.bshhsjdj
jasmenrojas249
 
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
advancepresentationskillshdhdhhdhdhdhhfhf
jasmenrojas249
 
Download iTop VPN Free 6.1.0.5882 Crack Full Activated Pre Latest 2025
imang66g
 
Key Features to Look for in Arizona App Development Services
Net-Craft.com
 
Summary Of Odoo 18.1 to 18.4 : The Way For Odoo 19
CandidRoot Solutions Private Limited
 
An Experience-Based Look at AI Lead Generation Pricing, Features & B2B Results
Thomas albart
 
WatchTraderHub - Watch Dealer software with inventory management and multi-ch...
WatchDealer Pavel
 
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
GALILEO CRS SYSTEM | GALILEO TRAVEL SOFTWARE
philipnathen82
 
Protecting the Digital World Cyber Securit
dnthakkar16
 
Presentation about Database and Database Administrator
abhishekchauhan86963
 
Salesforce Implementation Services Provider.pdf
VALiNTRY360
 
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
Generating Union types w/ Static Analysis
K. Matthew Dupree
 
49784907924775488180_LRN2959_Data_Pump_23ai.pdf
Abilash868456
 
Activate_Methodology_Summary presentatio
annapureddyn
 
Using licensed Data Loss Prevention (DLP) as a strategic proactive data secur...
Q-Advise
 
The-Dawn-of-AI-Reshaping-Our-World.pptxx
parthbhanushali307
 

Checking VirtualDub

  • 1. Checking VirtualDub Author: Andrey Karpov Date: 18.10.2013 Just recently I've checked the VirtualDub project with PVS-Studio. This was a random choice. You see, I believe that it is very important to regularly check and re-check various projects to show users that the PVS-Studio analyzer is evolving, and which project you run it on doesn't matter that much - bugs can be found everywhere. We already checked the VirtualDub project in 2011, but we found almost nothing of interest then. So, I decided to take a look at it now, 2 years later. I downloaded the archive VirtualDub-1.10.3-src.7z from the VirtualDub website. Analysis was performed by PVS-Studio 5.10. It took me just about one hour, so don't be strict with me. I surely must have missed something or, on the contrary, taken correct code fragments for incorrect ones. If you develop and maintain the VirtualDub project, please don't rely on my report - check it yourselves. We always help the open-source community and will grant you a registration key. I'm also asking Avery Lee to get me right. Last time his reaction to my mentioning VirtualDub in one of the articles was pretty negative. I never mean to say about any program that it's buggy. Software errors can be found in every program. My goal is to show how useful the static code analysis technology can be. At the same time, it will help to make open-source projects a bit more reliable. And that's wonderful. Single-time checks are of little use, of course. But that I cannot help, I'm afraid. Whether or not to use static analysis tools on a regular basis is up to developers. I can only try to explain why regular use is better. Here's one interesting post on the subject: Leo Tolstoy and static code analysis. However, this article is about bugs, not the static analysis methodology. Let's find out if there is anything interesting PVS-Studio has found in VirtualDub. Virtual destructors In C++, the destructor of a polymorphic base class must be declared virtual - this is the only way to ensure correct destruction of a derived object through a pointer to the corresponding base class. I know you know that. However, it still doesn't guarantee that you will never forget to declare the destructor virtual.
  • 2. There is the class VDDialogBaseW32 in VirtualDub: class VDDialogBaseW32 { .... ~VDDialogBaseW32(); .... virtual INT_PTR DlgProc(....) = 0; virtual bool PreNCDestroy(); .... } As you can see, it contains virtual functions. The destructor, however, is not declared virtual. And, naturally, there are some classes inherited from it, for example VDDialogAudioFilterFormatConvConfig: class VDDialogAudioFilterFormatConvConfig : public VDDialogBaseW32 { .... }; Here's the object destruction error: INT_PTR CALLBACK VDDialogBaseW32::StaticDlgProc(....) { VDDialogBaseW32 *pThis = (VDDialogBaseW32 *)GetWindowLongPtr(hwnd, DWLP_USER); .... delete pThis; .... } PVS-Studio's diagnostic message: V599 The destructor was not declared as a virtual one, although the 'VDDialogBaseW32' class contains virtual functions. VirtualDub gui.cpp 997 As you can see, a pointer to the base class is used to destroy the object. Doing it this way will cause an undefined behavior. The same trouble is with the class VDMPEGAudioPolyphaseFilter.
  • 3. A bit more about the undefined behavior It's all clear with bugs related to virtual destructors. Shift operations, however, is a more subtle subject. Take a look at the following example: void AVIVideoGIFOutputStream::write(....) { { .... for(int i=0; i<palsize; ++i) dict[i].mPrevAndLastChar = (-1 << 16) + i; .... } However hard one may try to convince me that this is an absolutely safe code which has been working for a dozen years, I'll keep saying that we are still having an undefined behavior here. Let's see what the standard has to say about such constructs: The shift operators << and >> group left-to-right. shift-expression << additive-expression shift-expression >> additive-expression The operands shall be of integral or unscoped enumeration type and integral promotions are performed. 1. The type of the result is that of the promoted left operand. The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand. 2. The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, the value of the result is E1 * 2^E2, reduced modulo one more than the maximum value representable in the result type. Otherwise, if E1 has a signed type and non-negative value, and E1*2^E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined. 3. 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. That the code works correctly is sheer luck, and it may suddenly change its behavior once you have switched over to a new compiler or started using compiler switches for optimization. See the article "Wade not in unknown waters. Part three" for more information about shift operations and code fixing in such cases. Here's the list of fragments of VirtualDub where PVS-Studio has detected Undefined behavior or Unspecified behavior.
  • 4. Misprints static ModuleInfo *CrashGetModules(void *&ptr) { .... while(*pszHeap++); if (pszHeap[-1]=='.') period = pszHeap-1; .... } PVS-Studio's diagnostic message: V529 Odd semicolon ';' after 'while' operator. VirtualDub crash.cpp 462 Note the semicolon after 'while'. It's either a mistake or incorrect code formatting. It seems more like the first thing. The loop "while(*pszHeap++);" will reach the end of the line and result in the 'pszHeap' variable pointing to the memory area after the terminal null. The check "if (pszHeap[-1]=='.')" is meaningless: it's the terminal null which is always found at "pszHeap[-1]". Here's another misprint when handling strings. void VDBackfaceService::Execute(...., char *s) { .... if (*s == '"') { while(*s && *s != '"') ++s; } else { .... } PVS-Studio's diagnostic message: V637 Two opposite conditions were encountered. The second condition is always false. Check lines: 183, 184. VirtualDub backface.cpp 183 This code must skip everything enclosed in the quotes. At least, it seems to do so. However, the condition (*s && *s != '"') is false right away. Perhaps the code should look like this: if (*s == '"') { ++s; while(*s && *s != '"') ++s;
  • 5. } The new operator throws exceptions when a memory allocation error occurs In old code you can often see checks of values returned by the new operator: int *p = new int[10]; if (!p) return false; Contemporary C++ compilers conforming to the C++ standard must throw an exception when memory cannot be allocated. You can set the 'new' operator not to do this, but this is outside the scope of our article now. Therefore, the check if (!p) is not necessary. This code is safe in general - just an odd check, that's all. But old code fragments may do you much harm, too. Take a look at the fragment from VirtualDub below. void HexEditor::Find(HWND hwndParent) { .... int *next = new int[nFindLength+1]; char *searchbuffer = new char[65536]; char *revstring = new char[nFindLength]; .... if (!next || !searchbuffer || !revstring) { delete[] next; delete[] searchbuffer; delete[] revstring; return; } .... } PVS-Studio's diagnostic message: V668 There is no sense in testing the 'next' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. VirtualDub hexviewer.cpp 2012
  • 6. If an exception is thrown in the line "char *revstring = new char[nFindLength];", a memory leak will occur. The delete[] operators won't be called. This is not a critical error, but it is worth mentioning. See the list of all the fragments of VirtualDub where a pointer is checked after calling the 'new' operator. A reference to a destroyed object vdlist_iterator& operator--(int) { vdlist_iterator tmp(*this); mp = mp->mListNodePrev; return tmp; } PVS-Studio's diagnostic message: V558 Function returns the reference to temporary local object: tmp. VirtualDub vdstl.h 460 The function is implemented incorrectly: it returns a reference to the local object 'tmp'. After leaving the function, this object will have been destroyed already; handling that reference will cause an undefined behavior. By the way, the ++ operator, standing nearby, is implemented correctly. First use, then check In various programs you can often see a bug when a pointer is first dereferenced and only then is checked for being NULL. These errors may stay hidden for a very long time because a pointer being null is a rare accident. VirtualDub also has a few of these. For example: void VDTContextD3D9::Shutdown() { .... mpData->mFenceManager.Shutdown(); .... if (mpData) { if (mpData->mhmodD3D9) FreeLibrary(mpData->mhmodD3D9); .... }
  • 7. PVS-Studio's diagnostic message: V595 The 'mpData' pointer was utilized before it was verified against nullptr. Check lines: 1422, 1429. Tessa context_d3d9.cpp 1422 The pointer "mpData" gets first dereferenced and then checked: "if (mpData)". These errors usually occur during code refactoring: the new code is inserted before the necessary checks. The other fragments which triggered the V595 diagnostic are listed here. Handling the HRESULT type VDPosition AVIReadTunnelStream::TimeToPosition(VDTime timeInUs) { AVISTREAMINFO asi; if (AVIStreamInfo(pas, &asi, sizeof asi)) return 0; return VDRoundToInt64(timeInUs * (double)asi.dwRate / (double)asi.dwScale * (1.0 / 1000000.0)); } PVS-Studio's diagnostic message: V545 Such conditional expression of 'if' operator is incorrect for the HRESULT type value 'AVIStreamInfoA(pas, & asi, sizeof asi)'. The SUCCEEDED or FAILED macro should be used instead. VirtualDub avireadhandlertunnelw32.cpp 230 The function AVIStreamInfo() returns a HRESULT value. This type cannot be interpreted as 'bool'. Information stored in a variable of the HRESULT type has a pretty complex structure, and to check a HRESULT value one needs to use either the SUCCEEDED or FAILED macros declared in "WinError.h". This is how they are implemented: #define FAILED(hr) (((HRESULT)(hr)) < 0) #define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) The fixed code should look like this: if (FAILED(AVIStreamInfo(pas, &asi, sizeof asi))) The same warning is generated on the following lines: • avireadhandlertunnelw32.cpp 238 • avireadhandlertunnelw32.cpp 335 • inputfileavi.cpp 440 • context_d3d11.cpp 959
  • 8. Magic numbers It's not a good idea to declare a string length as a number. You may easily make a mistaken when counting the characters. For example: bool VDOpenGLBinding::Attach(....) { .... if (!memcmp(start, "GL_EXT_blend_subtract", 20)) .... } PVS-Studio's diagnostic message: V512 A call of the 'memcmp' function will lead to underflow of the buffer '"GL_EXT_blend_subtract"'. Riza opengl.cpp 393 The length of the string "GL_EXT_blend_subtract" is 21 characters, not 20. This error is not critical; no troubles usually occur in practice. But you'd still better avoid such magic numbers and rather use a special macro to count the string length. For example: #define LiteralStrLen(S) (sizeof(S) / sizeof(S[0]) - 1) C++ allows you to create a template function which is safer: template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N]; template <typename T, size_t N> size_t LiteralStrLen(T (&array)[N]) { return sizeof(ArraySizeHelper(array)) - 1; } The advantage of the second method is that it doesn't allow you to accidentally pass a plain pointer as an argument. This method is described in detail in the article "PVS-Studio vs Chromium". Absolute paths VDDbgHelpDynamicLoaderW32::VDDbgHelpDynamicLoaderW32() { hmodDbgHelp = LoadLibrary( "c:program filesdebugging tools for windowsdbghelp");
  • 9. if (!hmodDbgHelp) { hmodDbgHelp = LoadLibrary("c:program files (x86)...... .... } PVS-Studio's diagnostic message: V631 Consider inspecting the 'LoadLibraryA' function call. Defining an absolute path to the file or directory is considered a poor style. VirtualDub leaks.cpp 67, 69 I guess you understand what is bad about this code. It has to do with debugging of course and doesn't seem to affect the end users in any way, but it's still better to get a correct path to Program Files. An incorrect argument sint64 rva; void tool_lookup(....) { .... printf("%08I64x %s + %x [%s:%d]n", addr, sym->name, addr-sym->rva, fn, line); .... } PVS-Studio's diagnostic message: V576 Incorrect format. Consider checking the fourth actual argument of the 'printf' function. The argument is expected to be not greater than 32-bit. Asuka lookup.cpp 56 The variable 'rva' is a 64-bit type, which means that it will write 8 bytes into the stack. The function printf() is a variadic function. The data type it must process is specified by the format string. In our case, the 'rva' variable will be processed as a 32-bit variable ("%x"). Whether or not this error will cause any faults depends on how in particular the compiler will be passing the arguments and on the platform bitness. For example, all the integer types in Win64 are first cast to a 64-bit type and only then are written into the stack, so there'll be no trouble with a variable occupying more stack memory than necessary. However, if the variable 'rva' stores values bigger than INT_MAX, its value will be incorrectly printed anyway. The same warning is generated for the following fragments: • dubstatus.cpp 360 • lookup.cpp 58
  • 10. Incorrect comparisons void VDVideoCompressorVCM::GetState(vdfastvector<uint8>& data) { DWORD res; .... res = ICGetState(hic, data.data(), size); .... if (res < 0) throw MyICError("Video compression", res); } PVS-Studio's diagnostic message: V547 Expression 'res < 0' is always false. Unsigned type value is never < 0. Riza w32videocodecpack.cpp 828 The variable 'res' is unsigned DWORD. It means that the "res < 0" expression will always give 'false'. A similar check can be found in w32videocodec.cpp 284. Here's one more bug of that kind. #define ICERR_CUSTOM -400L static const char *GetVCMErrorString(uint32 icErr) { .... if (icErr <= ICERR_CUSTOM) err = "A codec-specific error occurred."; .... } PVS-Studio's diagnostic message: V605 Consider verifying the expression: icErr <= - 400L. An unsigned value is compared to the number -400. system error_win32.cpp 54 The variable ' icErr' is 'unsigned', therefore the number '-400' will be implicitly cast to 'unsigned' before executing the comparison. As a result, the number '-400' will turn into 4294966896. Thus, the comparison (icErr <= -400) is equivalent to (icErr <= 4294966896). I guess this is far not what the programmer intended. Miscellaneous strange stuff void AVIOutputFile::finalize() {
  • 11. .... if (stream.mChunkCount && hdr.dwScale && stream.mChunkCount) .... } PVS-Studio's diagnostic message: V501 There are identical sub-expressions 'stream.mChunkCount' to the left and to the right of the '&&' operator. VirtualDub avioutputfile.cpp 761 The variable 'stream.mChunkCount' is checked twice. Either one of the checks is not necessary or something else should have been checked. void VDVideoCompressorVCM::Start(const void *inputFormat, uint32 inputFormatSize, const void *outputFormat, uint32 outputFormatSize, const VDFraction& frameRate, VDPosition frameCount) { this->hic = hic; .... } PVS-Studio's diagnostic message: V570 The 'this->hic' variable is assigned to itself. Riza w32videocodecpack.cpp 253 void VDDialogAudioConversionW32::RecomputeBandwidth() { .... if (IsDlgButtonChecked(mhdlg, IDC_PRECISION_NOCHANGE)) { if (mbSourcePrecisionKnown && mbSource16Bit) bps *= 2; else bps = 0;
  • 12. } if (IsDlgButtonChecked(mhdlg, IDC_PRECISION_16BIT)) bps *= 2; .... } PVS-Studio's diagnostic message: V646 Consider inspecting the application's logic. It's possible that 'else' keyword is missing. VirtualDub optdlg.cpp 120 Seems like incorrect code formatting. Or perhaps the keyword 'else' is missing. bool VDCaptureDriverScreen::Init(VDGUIHandle hParent) { .... mbAudioHardwarePresent = false; mbAudioHardwarePresent = true; .... } PVS-Studio's diagnostic message: V519 The 'mbAudioHardwarePresent' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 274, 275. VDCapture cap_screen.cpp 275 Conclusion As you can see, even running static analysis for once may be very useful. But it is much more efficient to run it regularly. Programmers keep compiler warnings turned on all the time, not only once before the release, don't they? It's the same with static analysis tools. Using them regularly allows you to eliminate any bugs as soon as they occur. Think of PVS-Studio as kind of an additional story over the compiler which generates some more worthy warnings. It's best of all to use the incremental analysis: it allows you to detect bugs in freshly modified files right after compilation.