SlideShare a Scribd company logo
Checking Wine with PVS-Studio and Clang 
Static Analyzer 
Author: Svyatoslav Razmyslov 
Date: 13.08.2014 
In this article, I'm going to tell you about the check of the Wine project done by the static analyzers for 
C/C++ code PVS-Studio and Clang Static Analyzer. 
Wine 
Wine (originally an acronym for "Wine Is Not an Emulator") enables Linux, Mac, FreeBSD, and Solaris 
users to run Windows applications without a copy of Microsoft Windows. Wine is free software under 
constant development. Other platforms may benefit as well. 
You can get the project's source code through the git clone command at 
git://source.winehq.org/git/wine.git 
About the analyzers used 
• PVS-Studio is a static analyzer detecting errors in the source code of C/C++/C++11 applications. 
We were using PVS-Studio 5.18 to check the Wine project. 
• Clang Static Analyzer is a static analyzer detecting errors in C, C++ and Objective-C applications. 
We were using Clang 3.4.2 release version for openSUSE 13.1 to check the Wine project. 
Analysis results by PVS-Studio 
Shifting a negative number 
V610 Undefined behavior. Check the shift operator '<<. The left operand '(LONGLONG) - 1' is negative. 
propvar.c 127 
... 
if (*res >= ((LONGLONG)1 << (dest_bits-1)) || 
*res < ((LONGLONG)-1 << (dest_bits-1))) 
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); 
... 
The LONGLONG type is declared as 'typedef signed __int64 LONGLONG;', i.e. it is a signed type. 
According to the new standard, shifting negative numbers leads to undefined or unspecified behavior.
To find out why such code still may work and how to fix it best, see the article Wade not in unknown 
waters - part three. 
Typos 
V501 There are identical sub-expressions '!lpScaleWindowExtEx->xNum' to the left and to the right of 
the '||' operator. enhmetafile.c 1418 
... 
if (!lpScaleWindowExtEx->xNum || !lpScaleWindowExtEx->xDenom || 
!lpScaleWindowExtEx->xNum || !lpScaleWindowExtEx->yDenom) 
break; 
... 
The lpScaleWindowExtEx->xNum condition is checked twice. Instead of one of the checks, the statement 
lpScaleWindowExtEx->yNum was most likely meant to be used. The corresponding field is found in the 
structure declaration: 
typedef struct { 
EMR emr; 
LONG xNum; //<== 
LONG xDenom; 
LONG yNum; //<== 
LONG yDenom; 
} EMRSCALEVIEWPORTEXTEX, *PEMRSCALEVIEWPORTEXTEX, 
EMRSCALEWINDOWEXTEX, *PEMRSCALEWINDOWEXTEX; 
V501 There are identical sub-expressions '!(types[i + 1] & PathPointTypeBezier)' to the left and to the 
right of the '||' operator. graphics.c 1751 
... 
for(i = 1; i < count; i++){ 
.... 
if((i + 2 >= count) || 
!(types[i + 1] & PathPointTypeBezier) || 
!(types[i + 1] & PathPointTypeBezier)){ 
.... 
} 
i += 2; 
} 
... 
This fragment was discovered by the same diagnostic V501, but this time it doesn't show the reason for 
identical conditions that clearly. Most likely, one of them should contain types[i + 2] because the 
programmer has checked before if the array item with the index larger than 'i' by 2 can be addressed. 
V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as 
following: 'A = (B != C)'. request.c 3354 
if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) 
return hr; 
if ((hr = SafeArrayGetUBound( sa, 1, &size ) != S_OK)) //<== 
{ 
SafeArrayUnaccessData( sa ); 
return hr; 
}
The precedence of the != operator is higher than that of the assignment operator =. And you can see 
clearly well from the condition above that the assignment should also be enclosed in another pair of 
parentheses as hr would otherwise get value 0 or 1. 
Another similar issue: 
V501 There are identical sub-expressions to the left and to the right of the '|' operator: VT_ARRAY | 
VT_ARRAY vartest.c 2161 
Cascade of conditional operators 
V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error 
presence. Check lines: 1754, 1765. msi.c 1754 
if (!strcmpW( szProperty, INSTALLPROPERTY_LOCALPACKAGEW )) //<== 
{ 
... 
} 
else if (!strcmpW( szProperty, INSTALLPROPERTY_INSTALLDATEW )) 
{ 
... 
} 
else 
if (!strcmpW( szProperty, INSTALLPROPERTY_LOCALPACKAGEW )) //<== 
{ 
... 
} 
else if (!strcmpW( szProperty, INSTALLPROPERTY_UNINSTALLABLEW ) || 
!strcmpW( szProperty, INSTALLPROPERTY_PATCHSTATEW ) || 
!strcmpW( szProperty, INSTALLPROPERTY_DISPLAYNAMEW ) || 
!strcmpW( szProperty, INSTALLPROPERTY_MOREINFOURLW )) 
{ 
... 
} 
else 
{ 
... 
} 
If identical conditions are checked in a cascade of conditional operators, those conditions never get 
control. Perhaps there is a typo in the constant INSTALLPROPERTY_* passed to this fragment. 
Equivalent branches of if operator 
V523 The 'then' statement is equivalent to the 'else' statement. filedlg.c 3302 
if(pDIStruct->itemID == liInfos->uSelectedItem) 
{ 
ilItemImage = (HIMAGELIST) SHGetFileInfoW ( 
(LPCWSTR) tmpFolder->pidlItem, 0, &sfi, sizeof (sfi), 
shgfi_flags ); 
} 
else 
{ 
ilItemImage = (HIMAGELIST) SHGetFileInfoW ( 
(LPCWSTR) tmpFolder->pidlItem, 0, &sfi, sizeof (sfi), 
shgfi_flags );
} 
This code is either excessive or contains a typo. 
V523 The 'then' statement is equivalent to the 'else' statement. genres.c 1130 
... 
if(win32) 
{ 
put_word(res, 0); /* Reserved */ 
/* FIXME: The ResType in the NEWHEADER structure should 
* contain 14 according to the MS win32 doc. This is 
* not the case with the BRC compiler and I really doubt 
* the latter. Putting one here is compliant to win16 spec, 
* but who knows the true value? 
*/ 
put_word(res, 1); /* ResType */ 
put_word(res, icog->nicon); 
for(ico = icog->iconlist; ico; ico = ico->next) 
{ 
... 
} 
} 
else /* win16 */ 
{ 
put_word(res, 0); /* Reserved */ 
put_word(res, 1); /* ResType */ 
put_word(res, icog->nicon); 
for(ico = icog->iconlist; ico; ico = ico->next) 
{ 
... 
} 
} 
... 
One of the repeating branches is commented. Perhaps this is an incomplete fragment, not an error, but I 
decided to point it out anyway. 
String length changing 
V692 An inappropriate attempt to append a null character to a string. To determine the length of a 
string by 'strlen' function correctly, a string ending with a null terminator should be used in the first 
place. appdefaults.c 390 
... 
section[strlen(section)] = '0'; /* remove last backslash */ 
... 
In this code, NULL character will actually be written into NULL character and nothing will change. For the 
strlen() function to work properly, the string must be already null-terminated. The comment suggests 
that the programmer probably wanted to cut off the last backslash. Then the code should look like this: 
section[strlen(section) - 1] = '0'; 
One counter for two loops 
V535 The variable 'i' is being used for this loop and for the outer loop. Check lines: 980, 1003. 
iphlpapi_main.c 1003
... 
for (i = 0; i < num_v6addrs; i++) //<== 
{ 
... 
for (i = 0; i < 8 && !done; i++) //<== 
{ 
... 
} 
... 
if (i < num_v6addrs - 1) 
{ 
prefix->Next = (IP_ADAPTER_PREFIX *)ptr; 
prefix = prefix->Next; 
} 
} 
... 
This fragment is suspicious: a nested loop is organized through the i variable which is also used in the 
external loop. 
Double type conversion 
In the next two examples, the *void pointer is cast to other types twice: first to char*, then to DWORD*, 
after which an offset is added. Either the expression lacks parentheses or the code is excessive. Whether 
or not there is an error here depends on how much the programmer wanted to increment the pointer. 
V650 Type casting operation is utilized 2 times in succession. Next, the '+' operation is executed. 
Probably meant: (T1)((T2)a + b). typelib.c 9147 
... 
struct WMSFT_SegContents arraydesc_seg; 
typedef struct tagWMSFT_SegContents { 
DWORD len; 
void *data; 
} WMSFT_SegContents; 
... 
DWORD offs = file->arraydesc_seg.len; 
DWORD *encoded; 
encoded = (DWORD*)((char*)file->arraydesc_seg.data) + offs;//<== 
Another similar issue: 
V650 Type casting operation is utilized 2 times in succession. Next, the '+' operation is executed. 
Probably meant: (T1)((T2)a + b). protocol.c 194 
INT WINAPI 
EnumProtocolsW(LPINT protocols, LPVOID buffer, LPDWORD buflen) 
{ 
... 
unsigned int string_offset; 
... 
pi[i].lpProtocol = (WCHAR*)(char*)buffer + string_offset;//<== 
... 
} 
Difference of unsigned numbers 
V555 The expression 'This->nStreams - nr > 0' will work as 'This->nStreams != nr'. editstream.c 172
static HRESULT 
AVIFILE_RemoveStream(IAVIEditStreamImpl* const This, DWORD nr) 
{ 
... 
This->nStreams--; 
if (This->nStreams - nr > 0) { //<== 
memmove(This->pStreams + nr, This->pStreams + nr + 1, 
(This->nStreams - nr) * sizeof(EditStreamTable)); 
} 
... 
} 
The nr variable has the unsigned type DWORD. Subtracting it will result in an unsigned value, too. If nr is 
larger than This->nStreams, then the condition will still be true. 
A similar issue: 
V555 The expression 'This->fInfo.dwStreams - nStream > 0' will work as 'This->fInfo.dwStreams != 
nStream'. avifile.c 469 
First the execution, then the trial 
V595 The 'decl' pointer was utilized before it was verified against nullptr. Check lines: 1411, 1417. 
parser.y 1411 
... 
var_t *v = decl->var; //<== 
expr_list_t *sizes = get_attrp(attrs, ATTR_SIZEIS); 
expr_list_t *lengs = get_attrp(attrs, ATTR_LENGTHIS); 
int sizeless; 
expr_t *dim; 
type_t **ptype; 
array_dims_t *arr = decl ? decl->array : NULL; //<== 
type_t *func_type = decl ? decl->func_type : NULL; //<== 
... 
First the value by pointer is taken, then it is checked. 
Other similar fragments: 
• V595 The 'pcbData' pointer was utilized before it was verified against nullptr. Check lines: 1859, 
1862. registry.c 1859 
• V595 The 'token_user' pointer was utilized before it was verified against nullptr. Check lines: 
206, 213. lsa.c 206 
• V595 The 'psp' pointer was utilized before it was verified against nullptr. Check lines: 2680, 
2689. propsheet.c 2680 
• V595 The 'lpFindInfo' pointer was utilized before it was verified against nullptr. Check lines: 
6285, 6289. listview.c 6285 
• V595 The 'compiland' pointer was utilized before it was verified against nullptr. Check lines: 287, 
294. symbol.c 287 
• V595 The 'graphics' pointer was utilized before it was verified against nullptr. Check lines: 2096, 
2112. graphics.c 2096 
• V595 The 'current' pointer was utilized before it was verified against nullptr. Check lines: 240, 
251. request.c 240
Printing the result of identical functions 
V681 The language standard does not define an order in which the 'tlb_read_byte' functions will be 
called during evaluation of arguments. tlb.c 650 
... 
printf("%2.2x %2.2xn", tlb_read_byte(), tlb_read_byte()); 
... 
According to the C++ language standard, the sequence of computing a function's actual arguments is not 
defined. Which function will be called first depends on the compiler, compilation parameters, etc. 
Unreliable tests 
Some of the modules' folders contain the test folder with source files for tests. Debug information is 
printed through the 'ok' macro. Here are a few suspicious fragments: 
V501 There are identical sub-expressions to the left and to the right of the '==' operator: 
ddsd3.lpSurface == ddsd3.lpSurface dsurface.c 272 
... 
ok(ddsd3.lpSurface == ddsd3.lpSurface, //<== 
"lpSurface from GetSurfaceDesc(%p) differs 
from the one returned by Lock(%p)n", 
ddsd3.lpSurface, ddsd2.lpSurface); //<== 
... 
It very much looks like a typo. I suspect this code should compare the same variables that are printed. 
V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a 
lower priority than the '==' operator. url.c 767 
... 
ok(size == no_callback ? 512 : 13, "size=%dn", size); 
... 
The precedence of the "==" operator is higher than that of '?:', so the size variable is not compared to 
the values 512 and 13. The expression is always true as it evaluates either to 512 or 13, which means 
this check doesn't check anything. 
Other similar fragments: 
• V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator 
has a lower priority than the '==' operator. string.c 1086 
• V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator 
has a lower priority than the '==' operator. string.c 1111 
• V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator 
has a lower priority than the '==' operator. reader.c 761 
• V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator 
has a lower priority than the '==' operator. protocol.c 2928 
• V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator 
has a lower priority than the '==' operator. dde.c 1594 
• V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator 
has a lower priority than the '==' operator. reader.c 761
Analysis results by Clang Static Analyzer 
Clang Static Analyzer implements the mechanism of searching for potential errors through parsing 
possible application execution branches. When a suspicious fragment is detected, the analyzer creates a 
report for this file in the HTML format (by default) or the PLIST format, with comments for a number of 
steps (from one to several dozens) which lead to that suspicious code fragment. 
Most of the messages I got while analyzing the Wine project were of the same kind: a variable is not 
initialized when declared; a function receiving a variable address does not initialize it in certain branches 
of the switch operator, or 'return' is executed before initialization. Such issues are not handled 
anywhere further in the code, and the program keeps using the uninitialized variable. There are 
hundreds of such issues in the project, so I won't discuss them in this article. Some of them, as I guess, 
may be real, critical bugs. But let's leave them lay on the authors' conscience. 
Uninitialized variable in a condition 
File: dlls/atl110/../atl/atl_ax.c 
Location: line 1092, column 10 
Description: Branch condition evaluates to a garbage value 
HRESULT 
WINAPI AtlAxCreateControlEx(LPCOLESTR lpszName, HWND hWnd, 
IStream *pStream, IUnknown **ppUnkContainer, 
IUnknown **ppUnkControl, REFIID iidSink, IUnknown *punkSink) 
{ 
... 
IUnknown *pContainer; 
... 
hRes = AtlAxAttachControl( pUnkControl, hWnd, &pContainer ); 
if ( FAILED( hRes ) ) 
WARN("cannot attach control to windown"); 
... 
if ( pContainer ) //<== 
//Clang: Branch condition evaluates to a garbage value 
IUnknown_Release( pContainer ); 
return S_OK; 
} 
The uninitialized variable pContainer is used in the condition after the call of AtlAxAttachControl. This 
function's description is given below. 
HRESULT 
WINAPI AtlAxAttachControl(IUnknown *control, HWND hWnd, 
IUnknown **container) 
{ 
HRESULT hr; 
... 
if (!control) 
return E_INVALIDARG;//<== 
hr = IOCS_Create( hWnd, control, container ); 
return hWnd ? hr : S_FALSE; 
} 
In this code, the E_INVALIDARG value may be returned before initializing the container variable. It will 
result in the AtlAxCreateControlEx function generating the warning and going on to work with the 
uninitialized variable.
Possible buffer overflow 
File: tools/widl/typegen.c 
Location: line 1158, column 28 
Description: String copy function overflows destination buffer 
static unsigned int write_new_procformatstring_type(...) 
{ 
char buffer[64]; 
... 
strcpy( buffer, "/* flags:" ); 
if (flags & MustSize) strcat( buffer, " must size," ); 
if (flags & MustFree) strcat( buffer, " must free," ); 
if (flags & IsPipe) strcat( buffer, " pipe," ); 
if (flags & IsIn) strcat( buffer, " in," ); 
if (flags & IsOut) strcat( buffer, " out," ); 
if (flags & IsReturn) strcat( buffer, " return," ); 
if (flags & IsBasetype) strcat( buffer, " base type," ); 
if (flags & IsByValue) strcat( buffer, " by value," ); 
if (flags & IsSimpleRef) strcat( buffer, " simple ref," ); 
... 
} 
Even if not all the conditions are true, you still risk getting a too lengthy string that won't suit the buffer. 
Potential memory leak 
File: libs/wpp/ppl.yy.c 
Location: line 4475, column 1 
Description: Potential memory leak 
static void macro_add_arg(int last) 
{ 
.. 
if(last || mep->args[mep->nargs-1][0]) 
{ 
yy_push_state(pp_macexp); 
push_buffer(NULL, NULL, NULL, last ? 2 : 1); 
ppy__scan_string(mep->args[mep->nargs-1]); 
//Clang: Calling 'ppy__scan_string' 
//Clang: Returned allocated memory 
} 
//Clang: Potential memory leak 
} 
The pyy__scan_string function has a non-used return value. Calling this function will anyway make the 
malloc() function return the value, and after it is called, memory must be freed. 
Let's see how the call of the pyy__scan_string function leads to calling malloc. 
YY_BUFFER_STATE ppy__scan_string (yyconst char * yystr ) 
{ 
return ppy__scan_bytes(yystr,strlen(yystr) ); 
} 
YY_BUFFER_STATE ppy__scan_bytes (yyconst char * yybytes, 
yy_size_t _yybytes_len ) 
{
YY_BUFFER_STATE b; 
char *buf; 
... 
buf = (char *) ppy_alloc(n ); 
... 
b = ppy__scan_buffer(buf,n ); 
... 
return b; 
} 
YY_BUFFER_STATE ppy__scan_buffer (char * base, yy_size_t size ) 
{ 
YY_BUFFER_STATE b; 
... 
b=(YY_BUFFER_STATE) ppy_alloc(sizeof(struct yy_buffer_state)); 
... 
return b; 
} 
void *ppy_alloc (yy_size_t size ) 
{ 
return (void *) malloc( size ); 
} 
Division by zero 
File: dlls/winex11.drv/palette.c 
Location: line 601, column 43 
Description: Division by zero 
#define NB_RESERVED_COLORS 20 
... 
static void X11DRV_PALETTE_FillDefaultColors(....) 
{ 
... 
int i = 0, idx = 0; 
int red, no_r, inc_r; 
... 
if (palette_size <= NB_RESERVED_COLORS) 
return; 
while (i*i*i < (palette_size - NB_RESERVED_COLORS)) i++; 
no_r = no_g = no_b = --i; 
... 
inc_r = (255 - NB_COLORCUBE_START_INDEX)/no_r; 
//Clang: Division by zero 
... 
} 
The code will continue executing if the palette_size variable is larger than or equal to 21. With the value 
21, the 'i' variable will be first incremented by one and then decremented by one. As a result, the 'i' 
variable will remain equal to zero, which will cause the division-by-zero error. 
Uninitialized array item 
File: dlls/avifil32/api.c 
Location: line 1753, column 10 
Description: Assigned value is garbage or undefined
#define MAX_AVISTREAMS 8 
... 
HRESULT WINAPI AVISaveVW(....int nStreams ....) 
{ 
... 
//Declaring 8-item array, [0..7] 
PAVISTREAM pInStreams[MAX_AVISTREAMS]; 
... 
if (nStreams >= MAX_AVISTREAMS) { 
WARN(...); 
return AVIERR_INTERNAL; 
} 
... 
//Initializing first 7 items, [0..6]. 
for (curStream = 0; curStream < nStreams; curStream++) { 
pInStreams[curStream] = NULL; 
pOutStreams[curStream] = NULL; 
} 
... 
for (curStream = 0; curStream < nStreams; curStream++) { 
... 
if (curStream + 1 >= nStreams) { 
/* move the others one up */ 
PAVISTREAM *ppas = &pInStreams[curStream]; 
int n = nStreams - (curStream + 1); 
do { 
*ppas = pInStreams[curStream + 1]; 
//Clang: Assigned value is garbage or undefined 
} while (--n); 
} 
... 
} 
... 
} 
In this code, an array of 8 items is declared. The code will continue executing as long as the nStreams 
variable is less than 8, i.e. 7 at most. All the loops in this function with the conditional statement 
(curStream < nStreams) fail to iterate through the last item, both before its initialization and when using 
it. The line Clang displayed the message on is just that very line where the eighth item with the index 7 is 
taken, as the (curStream + 1 >= nStreams) condition will be true at curStream==6 and nStreams==7. 
Addressing the pInStreams[curStream + 1] array will give us the last, previously uninitialized item. 
Null path 
File: dlls/crypt32/rootstore.c 
Location: line 413, column 10 
Description: Null pointer passed as an argument to a 'nonnull' parameter 
static BOOL import_certs_from_path(LPCSTR path, 
HCERTSTORE store, BOOL allow_dir) 
{ 
... 
fd = open(path, O_RDONLY);
//Clang: Null pointer passed as 
//an argument to a 'nonnull' parameter 
... 
} 
To understand why Clang suspects that NULL may get here, let's examine the fragment where this 
function is called: 
static BOOL import_certs_from_dir(LPCSTR path, HCERTSTORE store) 
{ 
... 
char *filebuf = NULL; 
//Clang: 'filebuf' initialized to a null pointer value 
struct dirent *entry; 
while ((entry = readdir(dir))) 
{ 
... 
size_t name_len = strlen(entry->d_name); 
//Calling function to change filebuf 
if (!check_buffer_resize(&filebuf, &bufsize, 
path_len + 1 + name_len + 1)) 
{ 
ERR(...); 
break; 
} 
snprintf(filebuf, bufsize, "%s/%s", path, entry->d_name); 
if (import_certs_from_path(filebuf, store, FALSE) && !ret) 
//Clang: Passing null pointer value via 1st parameter 'path' 
//Clang: Calling 'import_certs_from_path' 
ret = TRUE; 
... 
} 
} 
In this code, the check_buffer_resize function is called where either the value of the filebuf variable 
must change or FALSE must be returned; but the function may fail to change filebuf and return TRUE 
instead. Take a look at the function's code below: 
static BOOL check_buffer_resize(char **ptr_buf, 
size_t *buf_size, size_t check_size) 
{ 
if (check_size > *buf_size) 
{ 
... 
*ptr_buf = CryptMemAlloc(*buf_size); 
... 
} 
return TRUE; 
} 
The function contains only one condition where the ptr_buf variable is changed, and if this condition is 
false, the true return result will allow you to use this variable further on. 
A similar issue is with the memcpy() function: 
File: server/directory.c
Location: line 548, column 21 
Description: Null pointer passed as an argument to a 'nonnull' parameter 
Unreliable check 
File: dlls/advapi32/registry.c 
Location: line 1209, column 13 
Description: Array access (from variable 'str') results in a null pointer dereference 
LSTATUS WINAPI RegSetValueExW(...., const BYTE *data, .... ) 
{ 
... 
if (data && ((ULONG_PTR)data >> 16) == 0) 
//Assuming pointer value is null 
return ERROR_NOACCESS; 
if (count && is_string(type)) 
{ 
LPCWSTR str = (LPCWSTR)data; 
//Clang: 'str' initialized to a null pointer value 
if (str[count / sizeof(WCHAR) - 1] && 
!str[count / sizeof(WCHAR)]) 
//Clang: Array access (from variable 'str') results in 
//a null pointer dereference 
count += sizeof(WCHAR); 
} 
... 
} 
If the null pointer data gets here, the program will go on executing until addressing the str variable. 
Another similar issue: 
File: dlls/comctl32/comctl32undoc.c 
Location: line 964, column 12 
Description: Array access (from variable 'lpDest') results in a null pointer dereference 
Conclusion 
The PVS-Studio analyzer and Clang Static Analyzer compared in this article use different code analysis 
methodologies, so it is natural that we've got different yet useful results by both tools. 
I should note that Clang Static Analyzer's diagnostics are quite alike. In fact, it actually warns you about a 
variable having an incorrect value (a null pointer, a zero, uninitialized variable, etc.). Depending on the 
variable value and how this variable is used, the corresponding diagnostic message is formed. PVS-Studio 
offers a wider variety of diagnostic types and is good at catching various typos. 
Of course, I may have missed something when looking through the reports. That's why it would be much 
better if the project authors studied the reports by any of the analyzers themselves.

More Related Content

PDF
PVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio
 
PDF
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
PVS-Studio
 
PDF
Checking Oracle VM VirtualBox. Part 1
Andrey Karpov
 
PDF
A Spin-off: CryEngine 3 SDK Checked with CppCat
Andrey Karpov
 
PDF
A Slipshod Check of the Visual C++ 2013 Library (update 3)
Andrey Karpov
 
PDF
A few words about OpenSSL
PVS-Studio
 
PDF
Python and Ruby implementations compared by the error density
PVS-Studio
 
PDF
Checking the Cross-Platform Framework Cocos2d-x
Andrey Karpov
 
PVS-Studio is there to help CERN: analysis of Geant4 project
PVS-Studio
 
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
PVS-Studio
 
Checking Oracle VM VirtualBox. Part 1
Andrey Karpov
 
A Spin-off: CryEngine 3 SDK Checked with CppCat
Andrey Karpov
 
A Slipshod Check of the Visual C++ 2013 Library (update 3)
Andrey Karpov
 
A few words about OpenSSL
PVS-Studio
 
Python and Ruby implementations compared by the error density
PVS-Studio
 
Checking the Cross-Platform Framework Cocos2d-x
Andrey Karpov
 

What's hot (18)

PDF
Spring RTS Engine Checkup
PVS-Studio
 
PDF
A fresh eye on Oracle VM VirtualBox
PVS-Studio
 
PDF
Rechecking Apache HTTP Server
PVS-Studio
 
PDF
Checking WinMerge with PVS-Studio for the second time
PVS-Studio
 
PDF
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
Andrey Karpov
 
PDF
Analyzing the Dolphin-emu project
PVS-Studio
 
PDF
Analysis of Microsoft Code Contracts
PVS-Studio
 
PDF
Analysis of the Ultimate Toolbox project
PVS-Studio
 
PDF
The Unicorn's Travel to the Microcosm
Andrey Karpov
 
PDF
Analyzing Firebird 3.0
Ekaterina Milovidova
 
PDF
Checking 7-Zip with PVS-Studio analyzer
PVS-Studio
 
PDF
PVS-Studio Meets Octave
PVS-Studio
 
PDF
PLSQL
Srinath Reddy
 
PDF
PVS-Studio vs Chromium - Continuation
PVS-Studio
 
PDF
One definition rule - что это такое, и как с этим жить
Platonov Sergey
 
PDF
A Spin-off: Firebird Checked by PVS-Studio
Andrey Karpov
 
PPT
C Sharp Jn (3)
jahanullah
 
PPTX
Random stability in systemVerilog and UVM based testbench
Kashyap Adodariya
 
Spring RTS Engine Checkup
PVS-Studio
 
A fresh eye on Oracle VM VirtualBox
PVS-Studio
 
Rechecking Apache HTTP Server
PVS-Studio
 
Checking WinMerge with PVS-Studio for the second time
PVS-Studio
 
Rechecking TortoiseSVN with the PVS-Studio Code Analyzer
Andrey Karpov
 
Analyzing the Dolphin-emu project
PVS-Studio
 
Analysis of Microsoft Code Contracts
PVS-Studio
 
Analysis of the Ultimate Toolbox project
PVS-Studio
 
The Unicorn's Travel to the Microcosm
Andrey Karpov
 
Analyzing Firebird 3.0
Ekaterina Milovidova
 
Checking 7-Zip with PVS-Studio analyzer
PVS-Studio
 
PVS-Studio Meets Octave
PVS-Studio
 
PVS-Studio vs Chromium - Continuation
PVS-Studio
 
One definition rule - что это такое, и как с этим жить
Platonov Sergey
 
A Spin-off: Firebird Checked by PVS-Studio
Andrey Karpov
 
C Sharp Jn (3)
jahanullah
 
Random stability in systemVerilog and UVM based testbench
Kashyap Adodariya
 
Ad

Viewers also liked (6)

PDF
2007 03-16 modeling and static analysis of complex biological systems dsr
Debora Da Rosa
 
PDF
devsummit2009js
Hajime Morrita
 
PDF
clang-intro
Hajime Morrita
 
PDF
Clang Analyzer Tool Review
Doug Schuster
 
PPTX
Clang-tidy: путешествие внутрь AST C++
corehard_by
 
PDF
Boosting Developer Productivity with Clang
Samsung Open Source Group
 
2007 03-16 modeling and static analysis of complex biological systems dsr
Debora Da Rosa
 
devsummit2009js
Hajime Morrita
 
clang-intro
Hajime Morrita
 
Clang Analyzer Tool Review
Doug Schuster
 
Clang-tidy: путешествие внутрь AST C++
corehard_by
 
Boosting Developer Productivity with Clang
Samsung Open Source Group
 
Ad

Similar to Checking Wine with PVS-Studio and Clang Static Analyzer (20)

PDF
Analyzing ReactOS One More Time
PVS-Studio
 
PDF
Analyzing Wine: One Year Later
PVS-Studio
 
PDF
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 1
PVS-Studio
 
PDF
Re-checking the ReactOS project - a large report
PVS-Studio
 
PDF
Picking Mushrooms after Cppcheck
Andrey Karpov
 
PDF
Errors detected in the Visual C++ 2012 libraries
PVS-Studio
 
PDF
Analysis of the Ultimate Toolbox project
PVS-Studio
 
PDF
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' Request
PVS-Studio
 
PDF
PVS-Studio delved into the FreeBSD kernel
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
Checking the code of Valgrind dynamic analyzer by a static analyzer
PVS-Studio
 
PDF
Analyzing Firebird 3.0
PVS-Studio
 
PDF
Tesseract. Recognizing Errors in Recognition Software
Andrey Karpov
 
PDF
PVS-Studio vs Clang
Andrey Karpov
 
PDF
Why Windows 8 drivers are buggy
Andrey Karpov
 
PDF
Checking the World of Warcraft CMaNGOS open source server
PVS-Studio
 
PDF
Linux version of PVS-Studio couldn't help checking CodeLite
PVS-Studio
 
PDF
Microsoft opened the source code of Xamarin.Forms. We couldn't miss a chance ...
PVS-Studio
 
PDF
Explanations to the article on Copy-Paste
PVS-Studio
 
Analyzing ReactOS One More Time
PVS-Studio
 
Analyzing Wine: One Year Later
PVS-Studio
 
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 1
PVS-Studio
 
Re-checking the ReactOS project - a large report
PVS-Studio
 
Picking Mushrooms after Cppcheck
Andrey Karpov
 
Errors detected in the Visual C++ 2012 libraries
PVS-Studio
 
Analysis of the Ultimate Toolbox project
PVS-Studio
 
Checking the Code of LDAP-Server ReOpenLDAP on Our Readers' Request
PVS-Studio
 
PVS-Studio delved into the FreeBSD kernel
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
 
Checking the code of Valgrind dynamic analyzer by a static analyzer
PVS-Studio
 
Analyzing Firebird 3.0
PVS-Studio
 
Tesseract. Recognizing Errors in Recognition Software
Andrey Karpov
 
PVS-Studio vs Clang
Andrey Karpov
 
Why Windows 8 drivers are buggy
Andrey Karpov
 
Checking the World of Warcraft CMaNGOS open source server
PVS-Studio
 
Linux version of PVS-Studio couldn't help checking CodeLite
PVS-Studio
 
Microsoft opened the source code of Xamarin.Forms. We couldn't miss a chance ...
PVS-Studio
 
Explanations to the article on Copy-Paste
PVS-Studio
 

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
Zero, one, two, Freddy's coming for you
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
 
Zero, one, two, Freddy's coming for you
Andrey Karpov
 

Recently uploaded (20)

PDF
Generating Union types w/ Static Analysis
K. Matthew Dupree
 
PDF
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
PDF
Protecting the Digital World Cyber Securit
dnthakkar16
 
PDF
ChatPharo: an Open Architecture for Understanding How to Talk Live to LLMs
ESUG
 
PDF
Adobe Illustrator Crack Full Download (Latest Version 2025) Pre-Activated
imang66g
 
PDF
Download iTop VPN Free 6.1.0.5882 Crack Full Activated Pre Latest 2025
imang66g
 
PPTX
Maximizing Revenue with Marketo Measure: A Deep Dive into Multi-Touch Attribu...
bbedford2
 
PPTX
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
PPTX
Visualising Data with Scatterplots in IBM SPSS Statistics.pptx
Version 1 Analytics
 
PPTX
Explanation about Structures in C language.pptx
Veeral Rathod
 
PPTX
Can You Build Dashboards Using Open Source Visualization Tool.pptx
Varsha Nayak
 
PDF
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
PPTX
The-Dawn-of-AI-Reshaping-Our-World.pptxx
parthbhanushali307
 
PPTX
ConcordeApp: Engineering Global Impact & Unlocking Billions in Event ROI with AI
chastechaste14
 
PPTX
Web Testing.pptx528278vshbuqffqhhqiwnwuq
studylike474
 
PDF
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
PDF
Using licensed Data Loss Prevention (DLP) as a strategic proactive data secur...
Q-Advise
 
PDF
advancepresentationskillshdhdhhdhdhdhhfhf
jasmenrojas249
 
PDF
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
PDF
MiniTool Power Data Recovery Crack New Pre Activated Version Latest 2025
imang66g
 
Generating Union types w/ Static Analysis
K. Matthew Dupree
 
New Download FL Studio Crack Full Version [Latest 2025]
imang66g
 
Protecting the Digital World Cyber Securit
dnthakkar16
 
ChatPharo: an Open Architecture for Understanding How to Talk Live to LLMs
ESUG
 
Adobe Illustrator Crack Full Download (Latest Version 2025) Pre-Activated
imang66g
 
Download iTop VPN Free 6.1.0.5882 Crack Full Activated Pre Latest 2025
imang66g
 
Maximizing Revenue with Marketo Measure: A Deep Dive into Multi-Touch Attribu...
bbedford2
 
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
Visualising Data with Scatterplots in IBM SPSS Statistics.pptx
Version 1 Analytics
 
Explanation about Structures in C language.pptx
Veeral Rathod
 
Can You Build Dashboards Using Open Source Visualization Tool.pptx
Varsha Nayak
 
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
The-Dawn-of-AI-Reshaping-Our-World.pptxx
parthbhanushali307
 
ConcordeApp: Engineering Global Impact & Unlocking Billions in Event ROI with AI
chastechaste14
 
Web Testing.pptx528278vshbuqffqhhqiwnwuq
studylike474
 
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
Using licensed Data Loss Prevention (DLP) as a strategic proactive data secur...
Q-Advise
 
advancepresentationskillshdhdhhdhdhdhhfhf
jasmenrojas249
 
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
MiniTool Power Data Recovery Crack New Pre Activated Version Latest 2025
imang66g
 

Checking Wine with PVS-Studio and Clang Static Analyzer

  • 1. Checking Wine with PVS-Studio and Clang Static Analyzer Author: Svyatoslav Razmyslov Date: 13.08.2014 In this article, I'm going to tell you about the check of the Wine project done by the static analyzers for C/C++ code PVS-Studio and Clang Static Analyzer. Wine Wine (originally an acronym for "Wine Is Not an Emulator") enables Linux, Mac, FreeBSD, and Solaris users to run Windows applications without a copy of Microsoft Windows. Wine is free software under constant development. Other platforms may benefit as well. You can get the project's source code through the git clone command at git://source.winehq.org/git/wine.git About the analyzers used • PVS-Studio is a static analyzer detecting errors in the source code of C/C++/C++11 applications. We were using PVS-Studio 5.18 to check the Wine project. • Clang Static Analyzer is a static analyzer detecting errors in C, C++ and Objective-C applications. We were using Clang 3.4.2 release version for openSUSE 13.1 to check the Wine project. Analysis results by PVS-Studio Shifting a negative number V610 Undefined behavior. Check the shift operator '<<. The left operand '(LONGLONG) - 1' is negative. propvar.c 127 ... if (*res >= ((LONGLONG)1 << (dest_bits-1)) || *res < ((LONGLONG)-1 << (dest_bits-1))) return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW); ... The LONGLONG type is declared as 'typedef signed __int64 LONGLONG;', i.e. it is a signed type. According to the new standard, shifting negative numbers leads to undefined or unspecified behavior.
  • 2. To find out why such code still may work and how to fix it best, see the article Wade not in unknown waters - part three. Typos V501 There are identical sub-expressions '!lpScaleWindowExtEx->xNum' to the left and to the right of the '||' operator. enhmetafile.c 1418 ... if (!lpScaleWindowExtEx->xNum || !lpScaleWindowExtEx->xDenom || !lpScaleWindowExtEx->xNum || !lpScaleWindowExtEx->yDenom) break; ... The lpScaleWindowExtEx->xNum condition is checked twice. Instead of one of the checks, the statement lpScaleWindowExtEx->yNum was most likely meant to be used. The corresponding field is found in the structure declaration: typedef struct { EMR emr; LONG xNum; //<== LONG xDenom; LONG yNum; //<== LONG yDenom; } EMRSCALEVIEWPORTEXTEX, *PEMRSCALEVIEWPORTEXTEX, EMRSCALEWINDOWEXTEX, *PEMRSCALEWINDOWEXTEX; V501 There are identical sub-expressions '!(types[i + 1] & PathPointTypeBezier)' to the left and to the right of the '||' operator. graphics.c 1751 ... for(i = 1; i < count; i++){ .... if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier) || !(types[i + 1] & PathPointTypeBezier)){ .... } i += 2; } ... This fragment was discovered by the same diagnostic V501, but this time it doesn't show the reason for identical conditions that clearly. Most likely, one of them should contain types[i + 2] because the programmer has checked before if the array item with the index larger than 'i' by 2 can be addressed. V593 Consider reviewing the expression of the 'A = B != C' kind. The expression is calculated as following: 'A = (B != C)'. request.c 3354 if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr; if ((hr = SafeArrayGetUBound( sa, 1, &size ) != S_OK)) //<== { SafeArrayUnaccessData( sa ); return hr; }
  • 3. The precedence of the != operator is higher than that of the assignment operator =. And you can see clearly well from the condition above that the assignment should also be enclosed in another pair of parentheses as hr would otherwise get value 0 or 1. Another similar issue: V501 There are identical sub-expressions to the left and to the right of the '|' operator: VT_ARRAY | VT_ARRAY vartest.c 2161 Cascade of conditional operators V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 1754, 1765. msi.c 1754 if (!strcmpW( szProperty, INSTALLPROPERTY_LOCALPACKAGEW )) //<== { ... } else if (!strcmpW( szProperty, INSTALLPROPERTY_INSTALLDATEW )) { ... } else if (!strcmpW( szProperty, INSTALLPROPERTY_LOCALPACKAGEW )) //<== { ... } else if (!strcmpW( szProperty, INSTALLPROPERTY_UNINSTALLABLEW ) || !strcmpW( szProperty, INSTALLPROPERTY_PATCHSTATEW ) || !strcmpW( szProperty, INSTALLPROPERTY_DISPLAYNAMEW ) || !strcmpW( szProperty, INSTALLPROPERTY_MOREINFOURLW )) { ... } else { ... } If identical conditions are checked in a cascade of conditional operators, those conditions never get control. Perhaps there is a typo in the constant INSTALLPROPERTY_* passed to this fragment. Equivalent branches of if operator V523 The 'then' statement is equivalent to the 'else' statement. filedlg.c 3302 if(pDIStruct->itemID == liInfos->uSelectedItem) { ilItemImage = (HIMAGELIST) SHGetFileInfoW ( (LPCWSTR) tmpFolder->pidlItem, 0, &sfi, sizeof (sfi), shgfi_flags ); } else { ilItemImage = (HIMAGELIST) SHGetFileInfoW ( (LPCWSTR) tmpFolder->pidlItem, 0, &sfi, sizeof (sfi), shgfi_flags );
  • 4. } This code is either excessive or contains a typo. V523 The 'then' statement is equivalent to the 'else' statement. genres.c 1130 ... if(win32) { put_word(res, 0); /* Reserved */ /* FIXME: The ResType in the NEWHEADER structure should * contain 14 according to the MS win32 doc. This is * not the case with the BRC compiler and I really doubt * the latter. Putting one here is compliant to win16 spec, * but who knows the true value? */ put_word(res, 1); /* ResType */ put_word(res, icog->nicon); for(ico = icog->iconlist; ico; ico = ico->next) { ... } } else /* win16 */ { put_word(res, 0); /* Reserved */ put_word(res, 1); /* ResType */ put_word(res, icog->nicon); for(ico = icog->iconlist; ico; ico = ico->next) { ... } } ... One of the repeating branches is commented. Perhaps this is an incomplete fragment, not an error, but I decided to point it out anyway. String length changing V692 An inappropriate attempt to append a null character to a string. To determine the length of a string by 'strlen' function correctly, a string ending with a null terminator should be used in the first place. appdefaults.c 390 ... section[strlen(section)] = '0'; /* remove last backslash */ ... In this code, NULL character will actually be written into NULL character and nothing will change. For the strlen() function to work properly, the string must be already null-terminated. The comment suggests that the programmer probably wanted to cut off the last backslash. Then the code should look like this: section[strlen(section) - 1] = '0'; One counter for two loops V535 The variable 'i' is being used for this loop and for the outer loop. Check lines: 980, 1003. iphlpapi_main.c 1003
  • 5. ... for (i = 0; i < num_v6addrs; i++) //<== { ... for (i = 0; i < 8 && !done; i++) //<== { ... } ... if (i < num_v6addrs - 1) { prefix->Next = (IP_ADAPTER_PREFIX *)ptr; prefix = prefix->Next; } } ... This fragment is suspicious: a nested loop is organized through the i variable which is also used in the external loop. Double type conversion In the next two examples, the *void pointer is cast to other types twice: first to char*, then to DWORD*, after which an offset is added. Either the expression lacks parentheses or the code is excessive. Whether or not there is an error here depends on how much the programmer wanted to increment the pointer. V650 Type casting operation is utilized 2 times in succession. Next, the '+' operation is executed. Probably meant: (T1)((T2)a + b). typelib.c 9147 ... struct WMSFT_SegContents arraydesc_seg; typedef struct tagWMSFT_SegContents { DWORD len; void *data; } WMSFT_SegContents; ... DWORD offs = file->arraydesc_seg.len; DWORD *encoded; encoded = (DWORD*)((char*)file->arraydesc_seg.data) + offs;//<== Another similar issue: V650 Type casting operation is utilized 2 times in succession. Next, the '+' operation is executed. Probably meant: (T1)((T2)a + b). protocol.c 194 INT WINAPI EnumProtocolsW(LPINT protocols, LPVOID buffer, LPDWORD buflen) { ... unsigned int string_offset; ... pi[i].lpProtocol = (WCHAR*)(char*)buffer + string_offset;//<== ... } Difference of unsigned numbers V555 The expression 'This->nStreams - nr > 0' will work as 'This->nStreams != nr'. editstream.c 172
  • 6. static HRESULT AVIFILE_RemoveStream(IAVIEditStreamImpl* const This, DWORD nr) { ... This->nStreams--; if (This->nStreams - nr > 0) { //<== memmove(This->pStreams + nr, This->pStreams + nr + 1, (This->nStreams - nr) * sizeof(EditStreamTable)); } ... } The nr variable has the unsigned type DWORD. Subtracting it will result in an unsigned value, too. If nr is larger than This->nStreams, then the condition will still be true. A similar issue: V555 The expression 'This->fInfo.dwStreams - nStream > 0' will work as 'This->fInfo.dwStreams != nStream'. avifile.c 469 First the execution, then the trial V595 The 'decl' pointer was utilized before it was verified against nullptr. Check lines: 1411, 1417. parser.y 1411 ... var_t *v = decl->var; //<== expr_list_t *sizes = get_attrp(attrs, ATTR_SIZEIS); expr_list_t *lengs = get_attrp(attrs, ATTR_LENGTHIS); int sizeless; expr_t *dim; type_t **ptype; array_dims_t *arr = decl ? decl->array : NULL; //<== type_t *func_type = decl ? decl->func_type : NULL; //<== ... First the value by pointer is taken, then it is checked. Other similar fragments: • V595 The 'pcbData' pointer was utilized before it was verified against nullptr. Check lines: 1859, 1862. registry.c 1859 • V595 The 'token_user' pointer was utilized before it was verified against nullptr. Check lines: 206, 213. lsa.c 206 • V595 The 'psp' pointer was utilized before it was verified against nullptr. Check lines: 2680, 2689. propsheet.c 2680 • V595 The 'lpFindInfo' pointer was utilized before it was verified against nullptr. Check lines: 6285, 6289. listview.c 6285 • V595 The 'compiland' pointer was utilized before it was verified against nullptr. Check lines: 287, 294. symbol.c 287 • V595 The 'graphics' pointer was utilized before it was verified against nullptr. Check lines: 2096, 2112. graphics.c 2096 • V595 The 'current' pointer was utilized before it was verified against nullptr. Check lines: 240, 251. request.c 240
  • 7. Printing the result of identical functions V681 The language standard does not define an order in which the 'tlb_read_byte' functions will be called during evaluation of arguments. tlb.c 650 ... printf("%2.2x %2.2xn", tlb_read_byte(), tlb_read_byte()); ... According to the C++ language standard, the sequence of computing a function's actual arguments is not defined. Which function will be called first depends on the compiler, compilation parameters, etc. Unreliable tests Some of the modules' folders contain the test folder with source files for tests. Debug information is printed through the 'ok' macro. Here are a few suspicious fragments: V501 There are identical sub-expressions to the left and to the right of the '==' operator: ddsd3.lpSurface == ddsd3.lpSurface dsurface.c 272 ... ok(ddsd3.lpSurface == ddsd3.lpSurface, //<== "lpSurface from GetSurfaceDesc(%p) differs from the one returned by Lock(%p)n", ddsd3.lpSurface, ddsd2.lpSurface); //<== ... It very much looks like a typo. I suspect this code should compare the same variables that are printed. V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '==' operator. url.c 767 ... ok(size == no_callback ? 512 : 13, "size=%dn", size); ... The precedence of the "==" operator is higher than that of '?:', so the size variable is not compared to the values 512 and 13. The expression is always true as it evaluates either to 512 or 13, which means this check doesn't check anything. Other similar fragments: • V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '==' operator. string.c 1086 • V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '==' operator. string.c 1111 • V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '==' operator. reader.c 761 • V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '==' operator. protocol.c 2928 • V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '==' operator. dde.c 1594 • V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '==' operator. reader.c 761
  • 8. Analysis results by Clang Static Analyzer Clang Static Analyzer implements the mechanism of searching for potential errors through parsing possible application execution branches. When a suspicious fragment is detected, the analyzer creates a report for this file in the HTML format (by default) or the PLIST format, with comments for a number of steps (from one to several dozens) which lead to that suspicious code fragment. Most of the messages I got while analyzing the Wine project were of the same kind: a variable is not initialized when declared; a function receiving a variable address does not initialize it in certain branches of the switch operator, or 'return' is executed before initialization. Such issues are not handled anywhere further in the code, and the program keeps using the uninitialized variable. There are hundreds of such issues in the project, so I won't discuss them in this article. Some of them, as I guess, may be real, critical bugs. But let's leave them lay on the authors' conscience. Uninitialized variable in a condition File: dlls/atl110/../atl/atl_ax.c Location: line 1092, column 10 Description: Branch condition evaluates to a garbage value HRESULT WINAPI AtlAxCreateControlEx(LPCOLESTR lpszName, HWND hWnd, IStream *pStream, IUnknown **ppUnkContainer, IUnknown **ppUnkControl, REFIID iidSink, IUnknown *punkSink) { ... IUnknown *pContainer; ... hRes = AtlAxAttachControl( pUnkControl, hWnd, &pContainer ); if ( FAILED( hRes ) ) WARN("cannot attach control to windown"); ... if ( pContainer ) //<== //Clang: Branch condition evaluates to a garbage value IUnknown_Release( pContainer ); return S_OK; } The uninitialized variable pContainer is used in the condition after the call of AtlAxAttachControl. This function's description is given below. HRESULT WINAPI AtlAxAttachControl(IUnknown *control, HWND hWnd, IUnknown **container) { HRESULT hr; ... if (!control) return E_INVALIDARG;//<== hr = IOCS_Create( hWnd, control, container ); return hWnd ? hr : S_FALSE; } In this code, the E_INVALIDARG value may be returned before initializing the container variable. It will result in the AtlAxCreateControlEx function generating the warning and going on to work with the uninitialized variable.
  • 9. Possible buffer overflow File: tools/widl/typegen.c Location: line 1158, column 28 Description: String copy function overflows destination buffer static unsigned int write_new_procformatstring_type(...) { char buffer[64]; ... strcpy( buffer, "/* flags:" ); if (flags & MustSize) strcat( buffer, " must size," ); if (flags & MustFree) strcat( buffer, " must free," ); if (flags & IsPipe) strcat( buffer, " pipe," ); if (flags & IsIn) strcat( buffer, " in," ); if (flags & IsOut) strcat( buffer, " out," ); if (flags & IsReturn) strcat( buffer, " return," ); if (flags & IsBasetype) strcat( buffer, " base type," ); if (flags & IsByValue) strcat( buffer, " by value," ); if (flags & IsSimpleRef) strcat( buffer, " simple ref," ); ... } Even if not all the conditions are true, you still risk getting a too lengthy string that won't suit the buffer. Potential memory leak File: libs/wpp/ppl.yy.c Location: line 4475, column 1 Description: Potential memory leak static void macro_add_arg(int last) { .. if(last || mep->args[mep->nargs-1][0]) { yy_push_state(pp_macexp); push_buffer(NULL, NULL, NULL, last ? 2 : 1); ppy__scan_string(mep->args[mep->nargs-1]); //Clang: Calling 'ppy__scan_string' //Clang: Returned allocated memory } //Clang: Potential memory leak } The pyy__scan_string function has a non-used return value. Calling this function will anyway make the malloc() function return the value, and after it is called, memory must be freed. Let's see how the call of the pyy__scan_string function leads to calling malloc. YY_BUFFER_STATE ppy__scan_string (yyconst char * yystr ) { return ppy__scan_bytes(yystr,strlen(yystr) ); } YY_BUFFER_STATE ppy__scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len ) {
  • 10. YY_BUFFER_STATE b; char *buf; ... buf = (char *) ppy_alloc(n ); ... b = ppy__scan_buffer(buf,n ); ... return b; } YY_BUFFER_STATE ppy__scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; ... b=(YY_BUFFER_STATE) ppy_alloc(sizeof(struct yy_buffer_state)); ... return b; } void *ppy_alloc (yy_size_t size ) { return (void *) malloc( size ); } Division by zero File: dlls/winex11.drv/palette.c Location: line 601, column 43 Description: Division by zero #define NB_RESERVED_COLORS 20 ... static void X11DRV_PALETTE_FillDefaultColors(....) { ... int i = 0, idx = 0; int red, no_r, inc_r; ... if (palette_size <= NB_RESERVED_COLORS) return; while (i*i*i < (palette_size - NB_RESERVED_COLORS)) i++; no_r = no_g = no_b = --i; ... inc_r = (255 - NB_COLORCUBE_START_INDEX)/no_r; //Clang: Division by zero ... } The code will continue executing if the palette_size variable is larger than or equal to 21. With the value 21, the 'i' variable will be first incremented by one and then decremented by one. As a result, the 'i' variable will remain equal to zero, which will cause the division-by-zero error. Uninitialized array item File: dlls/avifil32/api.c Location: line 1753, column 10 Description: Assigned value is garbage or undefined
  • 11. #define MAX_AVISTREAMS 8 ... HRESULT WINAPI AVISaveVW(....int nStreams ....) { ... //Declaring 8-item array, [0..7] PAVISTREAM pInStreams[MAX_AVISTREAMS]; ... if (nStreams >= MAX_AVISTREAMS) { WARN(...); return AVIERR_INTERNAL; } ... //Initializing first 7 items, [0..6]. for (curStream = 0; curStream < nStreams; curStream++) { pInStreams[curStream] = NULL; pOutStreams[curStream] = NULL; } ... for (curStream = 0; curStream < nStreams; curStream++) { ... if (curStream + 1 >= nStreams) { /* move the others one up */ PAVISTREAM *ppas = &pInStreams[curStream]; int n = nStreams - (curStream + 1); do { *ppas = pInStreams[curStream + 1]; //Clang: Assigned value is garbage or undefined } while (--n); } ... } ... } In this code, an array of 8 items is declared. The code will continue executing as long as the nStreams variable is less than 8, i.e. 7 at most. All the loops in this function with the conditional statement (curStream < nStreams) fail to iterate through the last item, both before its initialization and when using it. The line Clang displayed the message on is just that very line where the eighth item with the index 7 is taken, as the (curStream + 1 >= nStreams) condition will be true at curStream==6 and nStreams==7. Addressing the pInStreams[curStream + 1] array will give us the last, previously uninitialized item. Null path File: dlls/crypt32/rootstore.c Location: line 413, column 10 Description: Null pointer passed as an argument to a 'nonnull' parameter static BOOL import_certs_from_path(LPCSTR path, HCERTSTORE store, BOOL allow_dir) { ... fd = open(path, O_RDONLY);
  • 12. //Clang: Null pointer passed as //an argument to a 'nonnull' parameter ... } To understand why Clang suspects that NULL may get here, let's examine the fragment where this function is called: static BOOL import_certs_from_dir(LPCSTR path, HCERTSTORE store) { ... char *filebuf = NULL; //Clang: 'filebuf' initialized to a null pointer value struct dirent *entry; while ((entry = readdir(dir))) { ... size_t name_len = strlen(entry->d_name); //Calling function to change filebuf if (!check_buffer_resize(&filebuf, &bufsize, path_len + 1 + name_len + 1)) { ERR(...); break; } snprintf(filebuf, bufsize, "%s/%s", path, entry->d_name); if (import_certs_from_path(filebuf, store, FALSE) && !ret) //Clang: Passing null pointer value via 1st parameter 'path' //Clang: Calling 'import_certs_from_path' ret = TRUE; ... } } In this code, the check_buffer_resize function is called where either the value of the filebuf variable must change or FALSE must be returned; but the function may fail to change filebuf and return TRUE instead. Take a look at the function's code below: static BOOL check_buffer_resize(char **ptr_buf, size_t *buf_size, size_t check_size) { if (check_size > *buf_size) { ... *ptr_buf = CryptMemAlloc(*buf_size); ... } return TRUE; } The function contains only one condition where the ptr_buf variable is changed, and if this condition is false, the true return result will allow you to use this variable further on. A similar issue is with the memcpy() function: File: server/directory.c
  • 13. Location: line 548, column 21 Description: Null pointer passed as an argument to a 'nonnull' parameter Unreliable check File: dlls/advapi32/registry.c Location: line 1209, column 13 Description: Array access (from variable 'str') results in a null pointer dereference LSTATUS WINAPI RegSetValueExW(...., const BYTE *data, .... ) { ... if (data && ((ULONG_PTR)data >> 16) == 0) //Assuming pointer value is null return ERROR_NOACCESS; if (count && is_string(type)) { LPCWSTR str = (LPCWSTR)data; //Clang: 'str' initialized to a null pointer value if (str[count / sizeof(WCHAR) - 1] && !str[count / sizeof(WCHAR)]) //Clang: Array access (from variable 'str') results in //a null pointer dereference count += sizeof(WCHAR); } ... } If the null pointer data gets here, the program will go on executing until addressing the str variable. Another similar issue: File: dlls/comctl32/comctl32undoc.c Location: line 964, column 12 Description: Array access (from variable 'lpDest') results in a null pointer dereference Conclusion The PVS-Studio analyzer and Clang Static Analyzer compared in this article use different code analysis methodologies, so it is natural that we've got different yet useful results by both tools. I should note that Clang Static Analyzer's diagnostics are quite alike. In fact, it actually warns you about a variable having an incorrect value (a null pointer, a zero, uninitialized variable, etc.). Depending on the variable value and how this variable is used, the corresponding diagnostic message is formed. PVS-Studio offers a wider variety of diagnostic types and is good at catching various typos. Of course, I may have missed something when looking through the reports. That's why it would be much better if the project authors studied the reports by any of the analyzers themselves.