We checked the header files that are part of the Embarcadero C ++ Builder XE3. In fact, this only means checking a small number of inline functions. Accordingly, quite a few suspicious places were found, but enough for a small note.
Introduction
We regularly
check open projects , as well as anything else that can be checked. For example, we checked the libraries that are included with Visual C ++ 2012. The result was a note: "
Errors were detected in the Visual C ++ 2012 libraries ."
The Visual C ++ distribution kit includes the source codes of the libraries. The situation with C ++ Builder is worse. There are only header files. Therefore, it was possible to check only some inline functions. However, I managed to find something interesting. Consider the relevant sections of the code.
Work with warnings
#pragma warning (disable: 4115)
#include <objbase.h>
#pragma warning (default: 4115)
Diagnostic message issued by PVS-Studio:
')
V665 Possibly, the usage of #pragma warning (default: X) 'is incorrect in this context. The '#pragma warning (push / pop)' should be used instead. Check lines: 16, 18. iaguid.h 18
It is not good to set the warning mode to the default state. The correct approach is to save and then restore the previous state. To do this, use "#pragma warning (push [, n])" and "#pragma warning (pop)".
Bad macro
#define SET_VTYPE_AND_VARREF (type, val) \
this-> vt = VT_ ## type | VT_BYREF; \
V_ ## type ## REF (this) = val;
TVariantT & operator = (System :: Currency * src)
{
Clear ();
if (src)
SET_VTYPE_AND_VARREF (CY,
reinterpret_cast <tagCY *> (& (src-> Val)));
return * this;
}
Diagnostic message issued by PVS-Studio:
V640 The code's operational logic does not correspond with its formatting. The second statement will always be executed. It is possible that curly brackets are missing. utilcls.h 1781
The SET_VTYPE_AND_VARREF macro is written poorly. Its contents are not framed by curly braces {}. As a result, the condition "if (src)" will apply only to the first line of the macro.
Indefinite behavior
#define _BITS_BYTE 8
template <class _Uint,
_Uint _Ax,
_Uint _Cx,
_Uint _Mx>
class linear_congruential
{
static _CONST_DATA int _Nw =
(_BITS_BYTE * sizeof (_Uint) + 31) / 32;
void seed (seed_seq & _Seq)
{
_Unt _Arr [3 + _Nw];
....
int _Lsh = _BITS_BYTE * sizeof (_Uint);
....
for (int _Idx = _Nw; 0 <--_ Idx;)
_Arr [3 + _Idx - 1] | =
_Arr [3 + _Idx] << _Lsh;
....
}
}
Diagnostic message issued by PVS-Studio:
V610 Instantiate linear_congruential <unsigned long, 40014, 0, 2147483563>: Undefined behavior. Check the shift operator '<<. The operand is the right operand. random 738
In this function, the variable '_Lsh' will take the value 32. You cannot shift the 32-bit types by more than 31 bits. Excerpt from the standard: the behavior of the left operand.
Also, the DXVABitMask macro is made in a dangerous way:
#define DXVABitMask (__ n) (~ ((~ 0) << __n))
We quote the corresponding line from the standard: Otherwise, if E1 has a signed type and non-negative value, and E1 * 2 ^ E2 is a representation of the result, then it is the resulting value; otherwise, the behavior is undefined.
Because of this macro, PVS-Studio issues several warnings. Example:
V610 Undefined behavior. Check the shift operator '<<. The left operand '(~ 0)' is negative. dxva.h 1080
More information about the shifts and uncertain behavior can be found in the article:
Not knowing the ford, do not climb into the water. Part ThreeUnaccounted change in the behavior of the new operator.
Found a lot of places where, after calling the 'new' operator, it is checked that the pointer is not NULL. Now it does not make sense and even harmful. In case of a memory allocation error, the operator 'new' throws an exception std :: bad_alloc.
You can call the operator 'new', which does not generate an exception. C ++ Builder even has a special macro for this:
#define NEW_NOTHROW (_bytes) new (nothrow) BYTE [_bytes]
However, apparently not everywhere it was possible to restore order with the allocation of memory. I will give a few examples:
inline void _bstr_t :: Assign (BSTR s) throw (_com_error)
{
if (m_Data! = NULL) {
m_Data-> Assign (s);
}
else {
m_Data = new Data_t (s, TRUE);
if (m_Data == NULL) {
_com_issue_error (E_OUTOFMEMORY);
}
}
}
Diagnostic message issued by PVS-Studio:
V668 has been defined as the m_Data. The exception will be generated in the case of memory allocation error. comutil.h 454
Line "_com_issue_error (E_OUTOFMEMORY);" never done. In case of an error, an exception std :: bad_alloc () will be generated.
static inline BYTE * __ CorHlprNewThrows (size_t bytes)
{
BYTE * pbMemory = new BYTE [bytes];
if (pbMemory == NULL)
__CorHlprThrowOOM ();
return pbMemory;
}
Diagnostic message issued by PVS-Studio:
V668 has been defined using the 'new' operator. The exception will be generated in the case of memory allocation error. corhlpr.h 56
template <class TYPE, class ARG_TYPE>
void CDXArray <TYPE, ARG_TYPE> :: SetSize (int nNewSize, int nGrowBy)
{
....
TYPE * pNewData = (TYPE *) new BYTE [nNewMax * sizeof (TYPE)];
// oh well, it's better than crashing
if (pNewData == NULL)
return;
....
}
Diagnostic message issued by PVS-Studio:
The pnewdata has been defined using the 'new' operator. The exception will be generated in the case of memory allocation error. dxtmpl.h 338
The rest of the code snippets are similar, and there is no point in bringing them. I will confine myself only to diagnostic messages:
- V668 against allocated allocated allocated allocated allocated allocated allocated allocated allocated allocated allocated allocated The exception will be generated in the case of memory allocation error. d3dx10math.inl 1008
- V668 against allocated allocated allocated allocated allocated allocated allocated allocated allocated allocated allocated allocated The exception will be generated in the case of memory allocation error. dxtmpl.h 123
- The pnewdata has been defined using the 'new' operator. The exception will be generated in the case of memory allocation error. dxtmpl.h 395
- V668 _ new _ _ _ pointer pointer pointer pointer pointer pointer pointer pointer pointer pointer pointer pointer _ _ _ _ _ _ _ _ The exception will be generated in the case of memory allocation error. dxtmpl.h 1126
- V668 has been defined as the "newBrush". The exception will be generated in the case of memory allocation error. gdiplusbrush.h 44
- V668 has been allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. gdiplusbrush.h 374
- V668 has been allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. gdiplusbrush.h 615
- V668 has been allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. gdiplusbrush.h 645
- V668 has been allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. gdipluspath.h 1196
- V668 has been allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. gdipluspath.h 1231
- V668 has been allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. gdipluspath.h 1372
- V668 has been allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. gdipluspath.h 1405
- V668 has been defined as using the 'new' operator. The exception will be generated in the case of memory allocation error. gdipluslinecaps.h 153
- V668 has been defined as the language of the operator. The exception will be generated in the case of memory allocation error. gdiplusgraphics.h 1415
- V668 has been defined using the 'new' operator. The exception will be generated in the case of memory allocation error. gdiplusregion.h 89
- V668 allocated F new F allocated allocated allocated allocated allocated allocated allocated allocated allocated allocated allocated allocated allocated 66 66 66 66 The exception will be generated in the case of memory allocation error. gdiplusfontcollection.h 57
- V668 has been allocated using the 'new' operator. The exception will be generated in the case of memory allocation error. gdiplusbitmap.h 334
- V668 has been defined as the “bitmap”. The exception will be generated in the case of memory allocation error. gdiplusbitmap.h 819
- V668 has been defined as the “bitmap”. The exception will be generated in the case of memory allocation error. gdiplusbitmap.h 862
- V668 has been defined as the “m_pData”. The exception will be generated in the case of memory allocation error. spcollec.h 266
- The pnewdata has been defined using the 'new' operator. The exception will be generated in the case of memory allocation error. spcollec.h 325
And this is only in inline functions! Imagine what's going on in * .cpp files. :)
Note
The moment I finished writing this article, Embarcadero C ++ Builder XE4 was released. However, this does not negate the benefits of the analysis done and well demonstrates the capabilities of PVS-Studio. Plus, I still waited a little bit with the publication of this article. Today we have released a new version of PVS-Studio, which already supports C ++ Builder XE4. This is a good reason to publish and suggest trying our code analyzer.
Conclusion
Thank you all for your attention. I hope the C ++ Builder developers will pay attention to us and will want to check the source codes of the compiler and libraries with PVS-Studio. In conclusion, I want to offer a few useful links:
- Description of the PVS-Studio tool . You can download a fully functional trial version.
- Andrey Karpov. C ++ Builder, build 64-bit applications and renaissance Viva64 .
- Our twitter @Code_Analysis . We publish many interesting links on the subject of C / C ++.
- About what PVS-Studio can do. Errors discovered in Open Source projects by PVS-Studio developers using static analysis .