{"id":381833,"date":"2024-06-29T03:54:21","date_gmt":"2024-06-29T03:54:21","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=381833"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=381833","title":{"rendered":"<span>Even small projects have bugs, or how PVS-Studio checked Blend2D<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>We often check large projects because it&#8217;s easier to find bugs there. What if we try PVS-Studio on a small project? In this article we analyze Blend2D \u2014 a library for vector 2D graphics. Let&#8217;s look at what we found.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/2c9\/24f\/e26\/2c924fe2637af273f963fef60b1a4e12.png\" alt=\"0894_Blend2d\/image1.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/2c9\/24f\/e26\/2c924fe2637af273f963fef60b1a4e12.png\"\/><\/p>\n<p>  <\/p>\n<h2 id=\"introduction\">Introduction<\/h2>\n<p>  <\/p>\n<p>It&#8217;s no secret that large projects have fascinating errors. It&#8217;s not just &#171;the larger the codebase is \u2013 the more errors we can find&#187;. It&#8217;s also a known fact that <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/0158\/\">the density of errors grows<\/a> along with the codebase. That&#8217;s why we love checking large projects \u2014 to treat you with a variety of &#171;yummy&#187; and tricky errors and typos. Besides, it&#8217;s always interesting to search through a huge project with lots of dependencies, legacy code, and other stuff.<\/p>\n<p>  <\/p>\n<p>Today I&#8217;m moving away from this tradition. I decided to take a small project and see what PVS-Studio can find there. I chose Blend2D \u2014 branch <em>master<\/em>, commit <a href=\"https:\/\/github.com\/blend2d\/blend2d\/tree\/c4847906ea9423fe365ccafcaff62a37df5de3aa\">c484790<\/a>.<a name=\"habracut\"><\/a><\/p>\n<p>  <\/p>\n<h2 id=\"blend2d\">Blend2D<\/h2>\n<p>  <\/p>\n<p>Blend2D is a 2D vector graphics engine. This small library written in C++ contains about 70,000 lines of code:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">--------------------------------------------------------------------- Language           files          blank        comment           code --------------------------------------------------------------------- C++                   97          12924           9481          43372 C\/C++ Header         137           8305          12971          25225<\/code><\/pre>\n<p>  <\/p>\n<p>This library allows you to create 2D images. To achieve high performance, the library developers used multithreaded rendering and a self-written rasterizer. Blend2D provides C and C++ API. You can read more about the project and capabilities of this library on the <a href=\"https:\/\/blend2d.com\/\">website<\/a>. Now let&#8217;s proceed to the errors that PVS-Studio found in the Blend2D source code.<\/p>\n<p>  <\/p>\n<h2 id=\"an-always-false-expression\">An always-false expression<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v547\/\">V547<\/a> Expression &#8216;h == 0&#8217; is always false. jpegcodec.cpp 252<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">BLResult blJpegDecoderImplProcessMarker(....) noexcept {   uint32_t h = blMemReadU16uBE(p + 1);   \/\/ ....   if (h == 0)     return blTraceError(BL_ERROR_JPEG_UNSUPPORTED_FEATURE);   \/\/ ....   impl->delayedHeight = (h == 0); \/\/ &lt;=   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<p>In this code fragment, the result of the <em>blMemReadU16uBE<\/em> function call is assigned to the <em>h<\/em> variable. Then if the <em>h == 0<\/em> check is true, we exit from the function&#8217;s body. During initialization <em>impl->delayedHeight<\/em>, the <em>h<\/em> variable has non-zero value. Thus, <em>impl->delayedHeight<\/em> is <em>false<\/em>.<\/p>\n<p>  <\/p>\n<h2 id=\"a-typo-in-the-functions-signature\">A typo in the function&#8217;s signature<\/h2>\n<p>  <\/p>\n<p>V557 [CERT-ARR30-C] Array overrun is possible. The &#8216;3&#8217; index is pointing beyond array bound. geometry_p.h 552<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">static BL_INLINE bool blIsCubicFlat(const BLPoint p[3], double f) {   if (p[3] == p[0]) {     \/\/ ....   }   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<p>In the signature of the <em>blIsCubicFlat<\/em> function, the <em>p<\/em> variable is declared as an array of 3 elements. Then, <em>p[3]<\/em> is calculated in the body of the <em>blMemReadU16uBE<\/em> function.<\/p>\n<p>  <\/p>\n<p>Declaring the <em>const BLPoint p[3]<\/em> argument in the function&#8217;s signature equals declaring <em>const BLPoint *p<\/em>. The specified size is a hint to the developer. The compiler doesn&#8217;t use the size in any way. Thus, array index out of bounds happens only if we pass an array of 3 or fewer elements to the function. If <em>blIsCubicFlat<\/em> receives an array of 4 elements or more, there is no array index out of bounds and the code works in a defined way. I looked at the<a href=\"https:\/\/github.com\/blend2d\/blend2d\/blob\/c4847906ea9423fe365ccafcaff62a37df5de3aa\/src\/blend2d\/pathstroke.cpp\"> <em>blIsCubicFlat<\/em> function call<\/a> and realized that the array of 4 elements is passed to this function. This means that there&#8217;s a mistake in the function&#8217;s signature \u2014 a typo in the value of the array size.<\/p>\n<p>  <\/p>\n<h2 id=\"an-extra-evaluation-due-to-an-incorrect-operator\">An extra evaluation due to an incorrect operator<\/h2>\n<p>  <\/p>\n<p>V792 The &#8216;_isTagged&#8217; function located to the right of the operator &#8216;&amp;&#8217; will be called regardless of the value of the left operand. Perhaps, it is better to use &#8216;&amp;&amp;&#8217;. style.h 209<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">BL_NODISCARD BL_INLINE bool isObject() const noexcept {   return (data.type > BL_STYLE_TYPE_SOLID) &amp; _isTagged(); }<\/code><\/pre>\n<p>  <\/p>\n<p>Here the analyzer suggests using the &amp;&amp; logical operator instead of bitwise &amp;. The thing is, when we use bitwise &amp;, both of its arguments are calculated regardless of what values are obtained. For example, if <em>(data.type > BL_STYLE_TYPE_SOLID)<\/em> is false, bitwise &amp; returns 0 for any value of the second argument. However, the <em>_isTagged<\/em> function is called anyway.<\/p>\n<p>  <\/p>\n<p>If <em>(data.type > BL_STYLE_TYPE_SOLID)<\/em> is false, then the result of the &amp;&amp; logical operator is also 0, regardless of the second argument. Here the <em>_isTagged<\/em> function is not called. <\/p>\n<p>  <\/p>\n<p>The only question is, do we want to call the <em>_isTagged<\/em> function always or only when it is necessary to calculate the result? This function may have some side effects, which we may want to use regardless of the calculation. To answer this question, I looked at the <em>_isTagged<\/em> function code:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">BL_NODISCARD BL_INLINE bool _isTagged(uint32_t styleType) const noexcept {<\/code><\/pre>\n<p>  <\/p>\n<p>As you see from the function&#8217;s signature, <em>_isTagged<\/em> has the <em>const<\/em> modifier. This means that the function has no side effects.<\/p>\n<p>  <\/p>\n<p>Thus, using logical &amp;&amp; instead of bitwise &amp; in this code fragment allows us to avoid an unnecessary function call and reduces the program&#8217;s execution time.<\/p>\n<p>  <\/p>\n<h2 id=\"a-redundant-check\">A redundant check<\/h2>\n<p>  <\/p>\n<p>V595 [CERT-EXP12-C] The &#8216;_threadPool&#8217; pointer was utilized before it was verified against nullptr. Check lines: 158, 164. rasterworkermanager.cpp 158<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">class BLRasterWorkerManager { public:   BLThreadPool* _threadPool;   uint32_t _workerCount;   \/\/ .... } \/\/ .... void BLRasterWorkerManager::reset() noexcept {   \/\/ ....   if (_workerCount) {     \/\/ ....     _threadPool->releaseThreads(_workerThreads, _workerCount);     _workerCount = 0;     \/\/ ....   }   if (_threadPool) {     _threadPool->release();     _threadPool = nullptr;   }   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<p>The <em>_threadPool<\/em> pointer is dereferenced and then it&#8217;s checked for <em>nullptr<\/em>. The question is: is it an error or just a redundant check? Let&#8217;s try to figure it out.<\/p>\n<p>  <\/p>\n<p>When I examined the code, I realized the check was indeed redundant. We can simplify the code a bit. The following invariant is executed for the <em>BLRasterWorkerManage<\/em> class: the <em>_threadPool<\/em> pointer is null only when the <em>_workerCount<\/em> field equals 0.<\/p>\n<p>  <\/p>\n<p>Besides the <em>reset<\/em> method, fields <em>workerCount<\/em> and <em>_threadPool<\/em> are modified in two places: in the constructor and in the <em>init<\/em> method. Let&#8217;s start with the constructor:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">BL_INLINE BLRasterWorkerManager() noexcept     : \/\/ ....       _threadPool(nullptr),       \/\/ ....       _workerCount(0),       \/\/ ....       {}<\/code><\/pre>\n<p>  <\/p>\n<p>Everything is easy here: we assign 0 to the <em>_workerCount<\/em> field, and <em>nullptr<\/em> to the <em>_threadPool<\/em> pointer. Invariant is obviously executed.<\/p>\n<p>  <\/p>\n<p>Not so easy with the <em>init<\/em> method:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">BLResult BLRasterWorkerManager::init(....) noexcept {   \/\/ ....   uint32_t workerCount = threadCount - 1;   \/\/ ....   if (workerCount) {     \/\/ ....     BLThreadPool* threadPool = nullptr;     if (initFlags &amp; BL_CONTEXT_CREATE_FLAG_ISOLATED_THREAD_POOL) {       threadPool = blThreadPoolCreate();       if (!threadPool)         return blTraceError(BL_ERROR_OUT_OF_MEMORY);     }     else {       threadPool = blThreadPoolGlobal();     }     \/\/ ....     uint32_t n = threadPool->acquireThreads(workerThreads,  workerCount, acquireThreadFlags, &amp;reason);     \/\/ ....     if (!n) {       threadPool->release();       threadPool = nullptr;       \/\/ ....     }     \/\/ ....     _threadPool = threadPool;     \/\/ ....     _workerCount = n;   }   else {   \/\/ ....   } }<\/code><\/pre>\n<p>  <\/p>\n<p>First, we calculate the value of the <em>workerCount<\/em> local variable. Don&#8217;t confuse it with the <em>_workerCount<\/em> field! If the variable&#8217;s value is 0, then the else branch is executed. In this branch, both fields remain unchanged. So, we&#8217;ll look only at the case where <em>workerCount<\/em> is not equal to 0 and the then branch is executed. In this case, first, the <em>threadPool<\/em> pointer (not <em>_threadPool<\/em>!) becomes equal to 0. Then, depending on a condition, this pointer is initialized by the result of calling either <em>blThreadPoolCreate<\/em> or <em>blThreadPoolGlobal<\/em>. If it&#8217;s the <em>blThreadPoolCreate<\/em> function and it returns <em>nullptr<\/em>, then the no-return <em>blTraceError<\/em> function is called. We are not interested in the further execution. The <em>blThreadPoolGlobal<\/em> function looks like this:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">static BLWrap&lt;BLInternalThreadPool> blGlobalThreadPool; BLThreadPool* blThreadPoolGlobal() noexcept { return &amp;blGlobalThreadPool; }<\/code><\/pre>\n<p>  <\/p>\n<p>This means that the <em>blThreadPoolGlobal<\/em> function returns a non-null pointer. Consequently, either we lose control over the code, or the <em>threadPool<\/em> pointer is not null. Let&#8217;s go further:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">uint32_t n = threadPool->acquireThreads(workerThreads, workerCount,  acquireThreadFlags, &amp;reason);<\/code><\/pre>\n<p>  <\/p>\n<p>Here, the value of the threads acquired is written to the <em>n<\/em> variable. The value may or may not be zero. <\/p>\n<p>  <\/p>\n<p>If <em>n<\/em> equals 0, the <em>threadPool<\/em> pointer is nulled. The <em>_threadPool<\/em> pointer is also nulled, the <em>_workerCount<\/em> field is assigned the value of the <em>n<\/em> variable \u2014 0. As a result: <em>_threadPool = nullptr, _workerCount = 0.<\/em> In this case, the invariant is true.<\/p>\n<p>  <\/p>\n<p>Now let&#8217;s assume <em>n<\/em> is not 0. In this case, the <em>threadPool<\/em> pointer remains non-null and its value is written to the <em>_threadPool<\/em> pointer. The <em>_workerCount<\/em> field is assigned non-zero value of <em>n<\/em>. As a result: <em>_threadPool<\/em> is not equal to <em>nullptr; _workerCount<\/em> is not equal to<em> 0.<\/em> In this case the invariant is also true.<\/p>\n<p>  <\/p>\n<p>So, the invariant is really true. We can use it and say that checks <em>(_workerCount)<\/em> and <em>(_threadPool)<\/em> are always both true or both false. So, we can simplify the code by combining two checks into one. Like that, for example:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">void BLRasterWorkerManager::reset() noexcept {   \/\/ ....   if (_workerCount) {     assert(_threadPool);     for (uint32_t i = 0; i &lt; _workerCount; i++)       _workDataStorage[i]->~BLRasterWorkData();     _threadPool->releaseThreads(_workerThreads, _workerCount);     _workerCount = 0;     _workerThreads = nullptr;     _workDataStorage = nullptr;     _threadPool->release();     _threadPool = nullptr;   }   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<h2 id=\"using-an-uninitialized-variable\">Using an uninitialized variable<\/h2>\n<p>  <\/p>\n<p>V573 [CERT-EXP53-CPP] Uninitialized variable &#8216;n&#8217; was used. The variable was used to initialize itself. pixelconverter.cpp 2210<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">static BLResult BL_CDECL bl_convert_multi_step(...., uint32_t w, ....) {   for (uint32_t y = h; y; y--) {       uint32_t i = w;        workOpt.origin.x = baseOriginX;       dstData = dstLine;       srcData = srcLine;        while (i) {         uint32_t n = blMin(n, intermediatePixelCount);          srcToIntermediate(&amp;ctx->first, intermediateData, 0,                            srcData, srcStride, n, 1, nullptr);         intermediateToDst(&amp;ctx->second, dstData, dstStride,                            intermediateData, 0, n, 1, &amp;workOpt);          dstData += n * dstBytesPerPixel;         srcData += n * srcBytesPerPixel;         workOpt.origin.x += int(n);          i -= n;       } }<\/code><\/pre>\n<p>  <\/p>\n<p>The following line triggered the analyzer: <\/p>\n<p>  <\/p>\n<p><em>uint32_t n = blMin(n, intermediatePixelCount);<\/em>.<\/p>\n<p>  <\/p>\n<p>Agree, it&#8217;s quite strange to declare a variable and use its uninitialized value. Looks like the developer wanted to write something like this:<\/p>\n<p>  <\/p>\n<p><em>uint32_t n = blMin(i, intermediatePixelCount);<\/em>.<\/p>\n<p>  <\/p>\n<p>This looks better \u2014 the <em>i<\/em> variable is modified in the loop and is also used in the condition of breaking the loop.<\/p>\n<p>  <\/p>\n<h2 id=\"an-always-true-check\">An always-true check<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v547\/\">V547<\/a> Expression &#8216;x >= 5&#8217; is always true. pngcodec.cpp 588<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">static void blPngDeinterlaceBits(....) noexcept {   \/\/ ....   uint32_t x = w;   \/\/ ....   switch (n) {     case 2: {       \/\/ ....       if (x &lt;= 4) break;       if (x >= 5) b = uint32_t(*d5++);       \/\/ ....     }   \/\/ ....   }   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Let&#8217;s assume that the value of the <em>n<\/em> variable is 2 and we go to the corresponding <em>switch<\/em> branch. If the value of the <em>x<\/em> variable is less than <em>5<\/em>, the loop breaks. This means that check <em>x >= 5<\/em> is always true.<\/p>\n<p>  <\/p>\n<p>It&#8217;s hard to say where the error is. Maybe this check is redundant and we need to remove it. Maybe the developer intended to compare <em>x<\/em> with another value. Here&#8217;s one of the possible fixes:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">static void blPngDeinterlaceBits(....) noexcept {   ....   uint32_t x = w;   ....   switch (n) {     case 2: {       \/\/ ....       if (x &lt;= 4) break;       b = uint32_t(*d5++);       \/\/ ....     }     \/\/ ....   }   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<h2 id=\"a-copy-paste-error\">A copy-paste error<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v524\/\">V524<\/a> It is odd that the body of &#8216;end&#8217; function is fully equivalent to the body of &#8216;begin&#8217; function. string.h 258<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">class BLString : public BLStringCore { public:   \/\/ ....   BL_NODISCARD   BL_INLINE const char* begin() const noexcept   {     return impl->data + impl->size;   }    BL_NODISCARD   BL_INLINE const char* end() const noexcept   {     return impl->data + impl->size;   }   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Obviously, a copy-paste error. When a developer implemented the <em>begin<\/em> method, they copied the <em>end<\/em> method and forgot to change the method&#8217;s body. Corrected version:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">BL_NODISCARD BL_INLINE const char* begin() const noexcept {   return impl->data; }<\/code><\/pre>\n<p>  <\/p>\n<p>I suppose the readers have a question: &#171;Wait, how did it happen? We usually write code from top to bottom. Why do you claim that the end method was copied and renamed into begin, and not vice versa?&#187; This question is quite logical, so I present a small investigation of this warning.<\/p>\n<p>  <\/p>\n<p>First, the <em>BLString<\/em> has the <em>data<\/em> method. It looks like this:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">BL_NODISCARD BL_INLINE const char* data() const noexcept { return impl->data; }<\/code><\/pre>\n<p>  <\/p>\n<p>And look at how many times it&#8217;s used:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/4ca\/15b\/816\/4ca15b8169622deb8223dc3ad7a6edfc.png\" alt=\"0894_Blend2d\/image3.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/4ca\/15b\/816\/4ca15b8169622deb8223dc3ad7a6edfc.png\"\/><\/p>\n<p>  <\/p>\n<p>At the same time the <em>begin<\/em> method is not used at all:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/7a5\/3d7\/b98\/7a53d7b98bd21e0d53ebd82f392da701.png\" alt=\"0894_Blend2d\/image5.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/7a5\/3d7\/b98\/7a53d7b98bd21e0d53ebd82f392da701.png\"\/><\/p>\n<p>  <\/p>\n<p>Second, I found the following comment before the <em>begin<\/em> method:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">\/\/! Returns a pointer to the beginning of string data (iterator compatibility)<\/code><\/pre>\n<p>  <\/p>\n<p>Now when we found all the evidence, let me tell you what happened.<\/p>\n<p>  <\/p>\n<p>The <em>BLString<\/em> class had the <em>data<\/em> and <em>end<\/em> methods. Everything was great. But then the Blend2D developers thought about <em>iterator compatibility.<\/em> In particular, they wanted to make the following fragment work:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">BLString str; for( auto symb : str ) { .... }<\/code><\/pre>\n<p>  <\/p>\n<p>The <em>BLString<\/em> class needed to have methods <em>begin<\/em> and <em>end<\/em>. So, the developers wrote the missing <em>begin<\/em> method. It&#8217;s more logical to copy the <em>data<\/em> method. It does the same thing as <em>begin<\/em>. But when developers support <em>iterator compatibility<\/em>, they don&#8217;t think about the <em>data<\/em> method at all. This method has nothing to do with it. Developers think about the <em>end<\/em> method. They need it for <em>iterator compatibility<\/em>, and it&#8217;s already implemented. So why not copy it? They did copy it, they forgot to change the body, and they got an error.<\/p>\n<p>  <\/p>\n<p>What does it lead to? Most likely, the <em>begin<\/em> method is not called directly, the <em>data<\/em> method is used instead. At the same time, the range-based <em>for<\/em> loop (the example above) still doesn&#8217;t work. The code is compiled but does not iterate through the string.<\/p>\n<p>  <\/p>\n<h2 id=\"another-copy-paste-error\">Another copy-paste error<\/h2>\n<p>  <\/p>\n<p>V523 The &#8216;then&#8217; statement is equivalent to the &#8216;else&#8217; statement. pixelconverter.cpp 1215<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">template&lt;typename PixelAccess, bool AlwaysUnaligned> static BLResult BL_CDECL bl_convert_argb32_from_prgb_any(....) {   for (uint32_t y = h; y != 0; y--) {     if (!AlwaysUnaligned &amp;&amp; blIsAligned(srcData, PixelAccess::kSize))     {       for (uint32_t i = w; i != 0; i--) {         uint32_t pix = PixelAccess::fetchA(srcData);         uint32_t r = (((pix >> rShift) &amp; rMask) * rScale) >> 16;         uint32_t g = (((pix >> gShift) &amp; gMask) * gScale) >> 8;         uint32_t b = (((pix >> bShift) &amp; bMask) * bScale) >> 8;         uint32_t a = (((pix >> aShift) &amp; aMask) * aScale) >> 24;          BLPixelOps::unpremultiply_rgb_8bit(r, g, b, a);         blMemWriteU32a(dstData, (a &lt;&lt; 24) | (r &lt;&lt; 16) | (g &lt;&lt; 8) | b);          dstData += 4;         srcData += PixelAccess::kSize;       }     }     else {       for (uint32_t i = w; i != 0; i--) {         uint32_t pix = PixelAccess::fetchA(srcData);         uint32_t r = (((pix >> rShift) &amp; rMask) * rScale) >> 16;         uint32_t g = (((pix >> gShift) &amp; gMask) * gScale) >> 8;         uint32_t b = (((pix >> bShift) &amp; bMask) * bScale) >> 8;         uint32_t a = (((pix >> aShift) &amp; aMask) * aScale) >> 24;          BLPixelOps::unpremultiply_rgb_8bit(r, g, b, a);         blMemWriteU32a(dstData, (a &lt;&lt; 24) | (r &lt;&lt; 16) | (g &lt;&lt; 8) | b);          dstData += 4;         srcData += PixelAccess::kSize;       }     }     \/\/ ....   } }<\/code><\/pre>\n<p>  <\/p>\n<p>Another example of a copy-paste error. In this code fragment, branches <em>else<\/em> and <em>then<\/em> are completely identical. Obviously, the developer forgot to change the code on one of the branches, but I can&#8217;t offer any fix here.<\/p>\n<p>  <\/p>\n<h2 id=\"an-idempotent-loop\">An idempotent loop<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v1044\/\">V1044<\/a> Loop break conditions do not depend on the number of iterations. otcmap.cpp 59<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">#if defined(__GNUC__)   #define BL_LIKELY(...) __builtin_expect(!!(__VA_ARGS__), 1)   #define BL_UNLIKELY(...) __builtin_expect(!!(__VA_ARGS__), 0) #else   #define BL_LIKELY(...) (__VA_ARGS__)   #define BL_UNLIKELY(...) (__VA_ARGS__) #endif .... static BLResult BL_CDECL mapTextToGlyphsFormat0(....) noexcept {   \/\/ ....   uint32_t* ptr = content;   uint32_t* end = content + count;   \/\/ ....   while (ptr != end) {     uint32_t codePoint = content[0];     uint32_t glyphId = codePoint &lt; 256                          ? uint32_t(glyphIdArray[codePoint].value())                          : uint32_t(0);     content[0] = glyphId;     if (BL_UNLIKELY(glyphId == 0)) {       if (!undefinedCount)         state->undefinedFirst = (size_t)(ptr - content);       undefinedCount++;     }   }   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<p>This code fragment may cause looping. Variables <em>ptr<\/em> and <em>end<\/em> don&#8217;t change within the loop. If condition <em>ptr != end<\/em> is true, we get an infinite loop. Looks like the developer forgot to add the <em>ptr<\/em> pointer increment. We can fix the code like this:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">while (ptr != end) {   uint32_t codePoint = content[0];   uint32_t glyphId = codePoint &lt; 256                        ? uint32_t(glyphIdArray[codePoint].value())                        : uint32_t(0);   content[0] = glyphId;   if (BL_UNLIKELY(glyphId == 0)) {     if (!undefinedCount)       state->undefinedFirst = (size_t)(ptr - content);     undefinedCount++;   }   ++ptr; }<\/code><\/pre>\n<p>  <\/p>\n<p>The analyzer issued another warning for this loop:<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v776\/\">V776<\/a> Potentially infinite loop. The variable in the loop exit condition &#8216;ptr != end&#8217; does not change its value between iterations. otcmap.cpp 59<\/p>\n<p>  <\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>  <\/p>\n<p>Of course, this project doesn&#8217;t have as many errors as large projects with about a million code lines. But we expected that.<\/p>\n<p>  <\/p>\n<p>However, this project has some impressive errors. What does this mean?<\/p>\n<p>  <\/p>\n<p>First, even small projects have errors. Which means, we need to find them and fix them \ud83d\ude42<\/p>\n<p>  <\/p>\n<p>Second, a small codebase is not a guarantee that all errors will be found during code review. Sometimes developers miss an error after reading the code several times.<\/p>\n<p>  <\/p>\n<p>But static analysis tools don&#8217;t miss them. A static analyzer is ready to search for errors in code at any time of the day. It doesn&#8217;t need to rest. And most importantly \u2014 its all-seeing eye spies every typo in code!<\/p>\n<p>  <\/p>\n<p>If you are interested in static analysis and PVS-Studio \u2014 it&#8217;s high time to try it. Just download a <a href=\"https:\/\/pvs-studio.com\/blend2d\">free version<\/a> of the analyzer. Thank you for reading!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/593161\/\"> https:\/\/habr.com\/ru\/articles\/593161\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>We often check large projects because it&#8217;s easier to find bugs there. What if we try PVS-Studio on a small project? In this article we analyze Blend2D \u2014 a library for vector 2D graphics. Let&#8217;s look at what we found.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/2c9\/24f\/e26\/2c924fe2637af273f963fef60b1a4e12.png\" alt=\"0894_Blend2d\/image1.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/2c9\/24f\/e26\/2c924fe2637af273f963fef60b1a4e12.png\"\/><\/p>\n<p>  <\/p>\n<h2 id=\"introduction\">Introduction<\/h2>\n<p>  <\/p>\n<p>It&#8217;s no secret that large projects have fascinating errors. It&#8217;s not just &#171;the larger the codebase is \u2013 the more errors we can find&#187;. It&#8217;s also a known fact that <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/0158\/\">the density of errors grows<\/a> along with the codebase. That&#8217;s why we love checking large projects \u2014 to treat you with a variety of &#171;yummy&#187; and tricky errors and typos. Besides, it&#8217;s always interesting to search through a huge project with lots of dependencies, legacy code, and other stuff.<\/p>\n<p>  <\/p>\n<p>Today I&#8217;m moving away from this tradition. I decided to take a small project and see what PVS-Studio can find there. I chose Blend2D \u2014 branch <em>master<\/em>, commit <a href=\"https:\/\/github.com\/blend2d\/blend2d\/tree\/c4847906ea9423fe365ccafcaff62a37df5de3aa\">c484790<\/a>.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-381833","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/381833","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=381833"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/381833\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=381833"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=381833"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=381833"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}