{"id":409185,"date":"2024-06-29T20:38:16","date_gmt":"2024-06-29T20:38:16","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=409185"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=409185","title":{"rendered":"<span>Static code analysis of the PMDK library collection by Intel and errors that are not actual errors<\/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<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/6b7\/517\/2ab\/6b75172ab5d594657f4fa07d3c98cca1.png\" alt=\"PVS-Studio, PMDK\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/6b7\/517\/2ab\/6b75172ab5d594657f4fa07d3c98cca1.png\"\/><\/div>\n<p>  We were asked to check a collection of open source PMDK libraries for developing and debugging applications with NVRAM support by PVS-Studio. Well, why not? Moreover, this is a small project in C and C++ with a total code base size of about 170 KLOC without comments. Which means, the results review won&#8217;t take much energy and time. Let&#8217;s go.<br \/>  <a name=\"habracut\"><\/a><br \/>  The PVS-Studio 7.08 tool will be used to analyze the source code. Of course, readers of our blog have long been familiar with our tool, so I won&#8217;t focus on it. For those who have visited us for the first time, I suggest you refer to the article &#171;<a href=\"https:\/\/www.viva64.com\/en\/b\/0633\/\">How to quickly check out interesting warnings given by the PVS-Studio analyzer for C and C++ code<\/a>?&#187; and <a href=\"https:\/\/www.viva64.com\/en\/pvs-studio-download\/\">try<\/a> the free trial version of the analyzer.<\/p>\n<p>  This time I will take a look inside the PMDK project and tell you about the errors and shortcomings that I&#8217;ve noticed. My inner feeling was telling me there weren&#8217;t many of them, which indicates a high quality of the project code. As for some peculiar things, I found several fragments of incorrect code, which nevertheless was working correctly :). What I mean will become clearer from the rest of the story.<\/p>\n<p>  So PMDK is a collection of open source libraries and tools designed to simplify the development, debugging, and management of applications that support NVRAM. Check out more details here: <a href=\"https:\/\/docs.pmem.io\/persistent-memory\/getting-started-guide\/what-is-pmdk\">PMDK Introduction<\/a>. The source code is available here: <a href=\"https:\/\/github.com\/pmem\/pmdk\">pmdk<\/a>.<\/p>\n<p>  Let&#8217;s see what errors and shortcomings I can find in it. I must say straight away that I wasn&#8217;t always attentive when analyzing the report and could have missed a lot. Therefore, I urge the authors of the project not to be guided by this article when correcting defects, but to double-check the code themselves. As for me, to write the article, it will be enough to cite what I noted while viewing the list of warnings :).<\/p>\n<h2>Incorrect code that works <\/h2>\n<p>  <\/p>\n<h3>Size of allocated memory<\/h3>\n<p>  Programmers often spend time debugging code when the program doesn&#8217;t behave as it should. However, sometimes there are cases when the program works correctly, but the code contains an error. The programmer just got lucky, and the error doesn&#8217;t reveal itself. In the PMDK project, I stumbled upon several such interesting cases, so I decided to gather them together in a separate section.<\/p>\n<pre><code class=\"cpp\">int main(int argc, char *argv[]) {   ....   struct pool *pop = malloc(sizeof(pop));   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V568 It&#8217;s odd that &#8216;sizeof()&#8217; operator evaluates the size of a pointer to a class, but not the size of the &#8216;pop&#8217; class object. util_ctl.c 717<\/p>\n<p>  A classic typo due to which the wrong amount of memory is allocated. The <i>sizeof<\/i> operator will return the size of the pointer to the structure instead of the size of this structure. The correct version is:<\/p>\n<pre><code class=\"cpp\">struct pool *pop = malloc(sizeof(pool));<\/code><\/pre>\n<p>  or<\/p>\n<pre><code class=\"cpp\">struct pool *pop = malloc(sizeof(*pop));<\/code><\/pre>\n<p>  However, this incorrectly written code works fine. The fact is that the <i>pool<\/i> structure contains exactly one pointer:<\/p>\n<pre><code class=\"cpp\">struct pool {   struct ctl *ctl; };<\/code><\/pre>\n<p>  It turns out that the structure takes exactly as much space as the pointer. So that&#8217;s all right.<\/p>\n<h3>String length<\/h3>\n<p>  Let&#8217;s move on to the next case where an error was made again using the <i>sizeof<\/i> operator.<\/p>\n<pre><code class=\"cpp\">typedef void *(*pmem2_memcpy_fn)(void *pmemdest, const void *src, size_t len,     unsigned flags);  static const char *initial_state = \"No code.\";  static int test_rwx_prot_map_priv_do_execute(const struct test_case *tc,   int argc, char *argv[]) {   ....   char *addr_map = pmem2_map_get_address(map);   map->memcpy_fn(addr_map, initial_state, sizeof(initial_state), 0);   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V579 [CWE-687] The memcpy_fn function receives the pointer and its size as arguments. It is possibly a mistake. Inspect the third argument. pmem2_map_prot.c 513<\/p>\n<p>  To copy a string, a pointer to a special copy function is used. Note the call to this function, or rather its third argument.<\/p>\n<p>  The programmer assumes that the <i>sizeof<\/i> operator will calculate the size of the string literal. But, in fact, it is the size of the pointer that is calculated again.<\/p>\n<p>  The lucky thing is that the string consists of 8 characters, and its size matches the size of the pointer if the 64-bit application is being built. As a result, all 8 characters of the string &#171;No code.&#187; will be copied successfully.<\/p>\n<p>  In fact, the situation is even more complicated and intriguing. The interpretation of this error depends on whether the author wanted to copy the terminal null or not. Let&#8217;s consider two scenarios.<\/p>\n<p>  <b>Scenario 1.<\/b> Terminal null had to be copied. This way, I&#8217;m wrong and this is not just a harmless bug that doesn&#8217;t manifest itself. Only 8 bytes were copied, not 9 bytes. There is no terminal null, and the consequences can&#8217;t be predicted. In this case, one can correct the code by changing the definition of the <i>initial_state<\/i> constant string as follows:<\/p>\n<pre><code class=\"cpp\">static const char initial_state [] = \"No code.\";<\/code><\/pre>\n<p>  Now the value of <i>sizeof(initial_state)<\/i> is 9.<\/p>\n<p>  <b>Scenario 2.<\/b> Terminal null is not required at all. For example, you can see this line of code below:<\/p>\n<pre><code class=\"cpp\">UT_ASSERTeq(memcmp(addr_map, initial_state, strlen(initial_state)), 0);<\/code><\/pre>\n<p>  As you can see, the <i>strlen<\/i> function returns 8 and terminal null is not involved in the comparison. Then it&#8217;s really good luck and all is well.<\/p>\n<h3>Bitwise shift<\/h3>\n<p>  The following example is related to the bitwise shift operation.<\/p>\n<pre><code class=\"cpp\">static int clo_parse_single_uint(struct benchmark_clo *clo, const char *arg, void *ptr) {   ....   uint64_t tmax = ~0 >> (64 - 8 * clo->type_uint.size);   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V610 [CWE-758] Unspecified behavior. Check the shift operator &#8216;>>&#8217;. The left operand &#8216;~0&#8217; is negative. clo.cpp 205<\/p>\n<p>  The result of shifting the negative value to the right depends on the compiler implementation. Therefore, although this code may work correctly and expectedly under all currently existing application compilation modes, it is still a piece of luck.<\/p>\n<h3>Operation precedence<\/h3>\n<p>  And let&#8217;s look at the last case related to the operation precedence.<\/p>\n<pre><code class=\"cpp\">#define BTT_CREATE_DEF_SIZE  (20 * 1UL &lt;&lt; 20) \/* 20 MB *\/<\/code><\/pre>\n<p>  PVS-Studio warning: V634 [CWE-783] The priority of the &#8216;*&#8217; operation is higher than that of the &#8216;&lt;&lt;&#8216; operation. It&#8217;s possible that parentheses should be used in the expression. bttcreate.c 204<\/p>\n<p>  To get a constant equal to 20 MB, the programmer decided to follow these steps:<\/p>\n<ul>\n<li>Shifted 1 by 20 bits to get the value 1048576, i.e. 1 MB.<\/li>\n<li>Multiplied 1 MB by 20.<\/li>\n<\/ul>\n<p>  In other words, the programmer thinks that the calculations occur like this: (20 * (1UL &lt;&lt; 20)).<\/p>\n<p>  But in fact, the priority of the multiplication operator is higher than the priority of the shift operator and the expression is calculated like this: ((20 * 1UL) &lt;&lt; 20).<\/p>\n<p>  Agree it is unlikely that the programmer wanted the expression to be calculated in such a sequence. There is no point in multiplying 20 by 1. So this is the case where the code doesn&#8217;t work the way the programmer intended.<\/p>\n<p>  But this error won&#8217;t manifest itself in any way. It doesn&#8217;t matter how to write it:<\/p>\n<ul>\n<li>(20 * 1UL &lt;&lt; 20)<\/li>\n<li>(20 * (1UL &lt;&lt; 20))<\/li>\n<li>((20 * 1UL) &lt;&lt; 20)<\/li>\n<\/ul>\n<p>  The result is <a href=\"https:\/\/godbolt.org\/z\/sPsoMT\">always the same<\/a>! The desired value 20971520 is always obtained and the program works perfectly correctly.<\/p>\n<h2>Other errors<\/h2>\n<p>  <\/p>\n<h3>Parentheses in the wrong place<\/h3>\n<p>  <\/p>\n<pre><code class=\"cpp\">#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004  static void enum_handles(int op) {   ....   NTSTATUS status;   while ((status = NtQuerySystemInformation(       SystemExtendedHandleInformation,       hndl_info, hi_size, &amp;req_size)         == STATUS_INFO_LENGTH_MISMATCH)) {     hi_size = req_size + 4096;     hndl_info = (PSYSTEM_HANDLE_INFORMATION_EX)REALLOC(hndl_info,         hi_size);   }   UT_ASSERT(status >= 0);   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V593 [CWE-783] Consider reviewing the expression of the &#8216;A = B == C&#8217; kind. The expression is calculated as follows: &#8216;A = (B == C)&#8217;. ut.c 641<\/p>\n<p>  Take a careful look here:<\/p>\n<pre><code class=\"cpp\">while ((status = NtQuerySystemInformation(....) == STATUS_INFO_LENGTH_MISMATCH))<\/code><\/pre>\n<p>  The programmer wanted to store the value returned from the <i>NtQuerySystemInformation<\/i> function in the <i>status<\/i> variable and then compare it with a constant.<\/p>\n<p>  The programmer probably knew that the priority of the comparison operator (==) is higher than that of the assignment operator (=), and therefore parentheses should be used. But probably made a mistake and put them in the wrong place. As a result, parentheses don&#8217;t help in any way. Correct code: <\/p>\n<pre><code class=\"cpp\">while ((status = NtQuerySystemInformation(....)) == STATUS_INFO_LENGTH_MISMATCH)<\/code><\/pre>\n<p>  Because of this error, the <i>UT_ASSERT<\/i> macro will never work. After all, the <i>status<\/i> variable always contains the result of comparison, i.e. false (0) or true (1). So the condition ([0..1] >= 0) is always true.<\/p>\n<h3>Potential memory leak<\/h3>\n<p>  <\/p>\n<pre><code class=\"cpp\">static enum pocli_ret pocli_args_obj_root(struct pocli_ctx *ctx, char *in, PMEMoid **oidp) {   char *input = strdup(in);   if (!input)     return POCLI_ERR_MALLOC;    if (!oidp)     return POCLI_ERR_PARS;   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V773 [CWE-401] The function was exited without releasing the &#8216;input&#8217; pointer. A memory leak is possible. pmemobjcli.c 238<\/p>\n<p>  If <i>oidp<\/i> turns out to be a null pointer, the copy of the string created by calling the <i>strdup<\/i> function will be lost. It is best to postpone the check until memory is allocated:<\/p>\n<pre><code class=\"cpp\">static enum pocli_ret pocli_args_obj_root(struct pocli_ctx *ctx, char *in, PMEMoid **oidp) {   if (!oidp)     return POCLI_ERR_PARS;    char *input = strdup(in);   if (!input)     return POCLI_ERR_MALLOC;   .... }<\/code><\/pre>\n<p>  Or one can explicitly free up memory:<\/p>\n<pre><code class=\"cpp\">static enum pocli_ret pocli_args_obj_root(struct pocli_ctx *ctx, char *in, PMEMoid **oidp) {   char *input = strdup(in);   if (!input)     return POCLI_ERR_MALLOC;    if (!oidp)   {     free(input);     return POCLI_ERR_PARS;   }   .... }<\/code><\/pre>\n<p>  <\/p>\n<h3>Potential overflow<\/h3>\n<p>  <\/p>\n<pre><code class=\"cpp\">typedef long long os_off_t;  void do_memcpy(...., int dest_off, ....., size_t mapped_len, .....) {   ....   LSEEK(fd, (os_off_t)(dest_off + (int)(mapped_len \/ 2)), SEEK_SET);   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V1028 [CWE-190] Possible overflow. Consider casting operands, not the result. memcpy_common.c 62<\/p>\n<p>  Explicit casting the addition result to the <i>os_off_t<\/i> type doesn&#8217;t make sense. First, this doesn&#8217;t protect against the potential overflow that can occur when two <i>int<\/i> values are added together. Second, the result of addition would have been perfectly extended to the <i>os_off_t <\/i>type implicitly. Explicit type casting is simply redundant.<\/p>\n<p>  I think it would be more correct to write this way:<\/p>\n<pre><code class=\"cpp\">LSEEK(fd, dest_off + (os_off_t)(mapped_len) \/ 2, SEEK_SET);<\/code><\/pre>\n<p>  Here an unsigned value of the <i>size_t<\/i> type is converted to a signed value (to avoid a warning from the compiler). At the same time, overflow won&#8217;t occur when adding.<\/p>\n<h3>Incorrect protection against overflow<\/h3>\n<p>  <\/p>\n<pre><code class=\"cpp\">static DWORD get_rel_wait(const struct timespec *abstime) {   struct __timeb64 t;   _ftime64_s(&amp;t);   time_t now_ms = t.time * 1000 + t.millitm;   time_t ms = (time_t)(abstime->tv_sec * 1000 +     abstime->tv_nsec \/ 1000000);    DWORD rel_wait = (DWORD)(ms - now_ms);    return rel_wait &lt; 0 ? 0 : rel_wait; }<\/code><\/pre>\n<p>  PVS-Studio warning: V547 [CWE-570] Expression &#8216;rel_wait &lt; 0&#8217; is always false. Unsigned type value is never &lt; 0. os_thread_windows.c 359<\/p>\n<p>  It&#8217;s not very clear to me what is this case which the check should protect us from. Anyway, the check doesn&#8217;t work. The <i>rel_wait<\/i> variable is of the <i>DWORD<\/i> unsigned type. This means that <i>rel_wait &lt; 0<\/i> doesn&#8217;t make sense, since the result is always true.<\/p>\n<h3>Missing check that memory was successfully allocated<\/h3>\n<p>  Checking that memory is allocated is performed using <i>assert<\/i> macros, which do nothing if the Release version of the application is compiled. So we can say that there is no handling of the situation when <i>malloc<\/i> calls return <i>NULL<\/i>. Example:<\/p>\n<pre><code class=\"cpp\">static void remove_extra_node(TOID(struct tree_map_node) *node) {   ....   unsigned char *new_key = (unsigned char *)malloc(new_key_size);   assert(new_key != NULL);   memcpy(new_key, D_RO(tmp)->key, D_RO(tmp)->key_size);   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V575 [CWE-628] The potential null pointer is passed into &#8216;memcpy&#8217; function. Inspect the first argument. Check lines: 340, 338. rtree_map.c 340<\/p>\n<p>  There is even no <i>assert<\/i> in other places:<\/p>\n<pre><code class=\"cpp\">static void calc_pi_mt(void) {   ....   HANDLE *workers = (HANDLE *) malloc(sizeof(HANDLE) * pending);   for (i = 0; i &lt; pending; ++i) {     workers[i] = CreateThread(NULL, 0, calc_pi,       &amp;tasks[i], 0, NULL);     if (workers[i] == NULL)       break;   }   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V522 [CWE-690] There might be dereferencing of a potential null pointer &#8216;workers&#8217;. Check lines: 126, 124. pi.c 126<\/p>\n<p>  I counted at least 37 of such code fragments. So I don&#8217;t see the point in listing all of them in the article.<\/p>\n<p>  At a first glance, the lack of checks can be considered self-indulgence and smelly code. I don&#8217;t go along with this point of view. Programmers underestimate the danger of missing such checks. A null pointer won&#8217;t necessarily immediately manifest itself as a crash when dereferencing. The consequences can be more bizarre and dangerous, especially in multithreaded programs. To understand more about what is happening and why checks are needed, I strongly recommend that everyone read the article &#171;<a href=\"https:\/\/www.viva64.com\/en\/b\/0558\/\">Why it is important to check what the malloc function returned<\/a>&#171;.<\/p>\n<h2>Code smell<\/h2>\n<p>  <\/p>\n<h3>Double call of CloseHandle<\/h3>\n<p>  <\/p>\n<pre><code class=\"cpp\">static void prepare_map(struct pmem2_map **map_ptr,   struct pmem2_config *cfg, struct pmem2_source *src) {   ....   HANDLE mh = CreateFileMapping(....);   ....   UT_ASSERTne(CloseHandle(mh), 0);   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V586 [CWE-675] The &#8216;CloseHandle&#8217; function is called twice for deallocation of the same resource. pmem2_map.c 76<\/p>\n<p>  Looking at this code and the PVS-Studio warning, it is clear that nothing is clear. Where is double call of <i>CloseHandle<\/i> possible here? To find the answer, let&#8217;s look at the implementation of the <i>UT_ASSERTne<\/i> macro.<\/p>\n<pre><code class=\"cpp\">#define UT_ASSERTne(lhs, rhs)\\   do {\\     \/* See comment in UT_ASSERT. *\/\\     if (__builtin_constant_p(lhs) &amp;&amp; __builtin_constant_p(rhs))\\       UT_ASSERT_COMPILE_ERROR_ON((lhs) != (rhs));\\     UT_ASSERTne_rt(lhs, rhs);\\   } while (0)<\/code><\/pre>\n<p>  It didn&#8217;t get much clearer. What is <i>UT_ASSERT_COMPILE_ERROR_ON<\/i>? What is <i>UT_ASSERTne_rt<\/i>?<\/p>\n<p>  I&#8217;m not going to clutter the article with description of each macro and torture a reader by forcing to nest one macro into another in their head. Let&#8217;s look at the final version of the expanded code from the preprocessed file.<\/p>\n<pre><code class=\"cpp\">do {   if (0 &amp;&amp; 0) (void)((CloseHandle(mh)) != (0));   ((void)(((CloseHandle(mh)) != (0)) ||     (ut_fatal(\".....\", 76, __FUNCTION__, \"......: %s (0x%llx) != %s (0x%llx)\",               \"CloseHandle(mh)\", (unsigned long long)(CloseHandle(mh)), \"0\",               (unsigned long long)(0)), 0))); } while (0);<\/code><\/pre>\n<p>  Let&#8217;s delete the always false condition 0 &amp;&amp; 0) and every part that&#8217;s irrelevant. Here&#8217;s what we get:<\/p>\n<pre><code class=\"cpp\">((void)(((CloseHandle(mh)) != (0)) ||   (ut_fatal(...., \"assertion failure: %s (0x%llx) != %s (0x%llx)\",             ....., (unsigned long long)(CloseHandle(mh)), .... ), 0)));<\/code><\/pre>\n<p>  The handle is closed. If an error occurs, a debugging message is generated and <i>CloseHandle<\/i> is called for the same incorrect handle to get the error code again.<\/p>\n<p>  There seems to be no mistake. Once the handle is invalid, it&#8217;s okay that the <i>CloseHandle<\/i> function is called twice for it. However, this code has a smell, indeed. It would be more ideologically correct to call the function only once and save the status that it returned, so that if necessary, it can display its value in the message.<\/p>\n<h3>The mismatch between the interface of the implementation (constness dropping)<\/h3>\n<p>  <\/p>\n<pre><code class=\"cpp\">static int status_push(PMEMpoolcheck *ppc, struct check_status *st, uint32_t question) {   ....   } else {     status_msg_info_and_question(st->msg);            \/\/ &lt;=     st->question = question;     ppc->result = CHECK_RESULT_ASK_QUESTIONS;     st->answer = PMEMPOOL_CHECK_ANSWER_EMPTY;     PMDK_TAILQ_INSERT_TAIL(&amp;ppc->data->questions, st, next);   }   .... }<\/code><\/pre>\n<p>  The analyzer issues the message: V530 [CWE-252] The return value of function &#8216;status_msg_info_and_question&#8217; is required to be utilized. check_util.c 293<\/p>\n<p>  The reason is that the <i>status_msg_info_and_question<\/i> function, from the analyzer&#8217;s point of view, doesn&#8217;t change the state of objects external to it, including the passed constant string. In other words, the function just counts something and returns the result. And if so, it is strange not to use the result that this function returns. Although the analyzer is wrong this time, it points to the code smell. Let&#8217;s see how the called <i>status_msg_info_and_question<\/i> function works.<\/p>\n<pre><code class=\"cpp\">static inline int status_msg_info_and_question(const char *msg) {   char *sep = strchr(msg, MSG_SEPARATOR);   if (sep) {     *sep = ' ';     return 0;   }   return -1; }<\/code><\/pre>\n<p>  When calling the <i>strchr<\/i> function, constness is implicitly cast away. The fact is that in C it is declared as follows:<\/p>\n<pre><code class=\"cpp\">char * strchr ( const char *, int );<\/code><\/pre>\n<p>  Not the best solution. But the C language is the way it is :).<\/p>\n<p>  The analyzer got confused and didn&#8217;t get that the passed string was actually being changed. If this is the case, then the return value is not the most important one and you don&#8217;t need to use it.<\/p>\n<p>  However, even though the analyzer got confused, it points to a code smell. What confuses the analyzer can also confuse the person who maintains the code. It would be better to declare the function more honestly by removing <i>const<\/i>:<\/p>\n<pre><code class=\"cpp\">static inline int status_msg_info_and_question(char *msg) {   char *sep = strchr(msg, MSG_SEPARATOR);   if (sep) {     *sep = ' ';     return 0;   }   return -1; }<\/code><\/pre>\n<p>  This way the intent is immediately clear, and the analyzer will be silent.<\/p>\n<h3>Overcomplicated code<\/h3>\n<p>  <\/p>\n<pre><code class=\"cpp\">static struct memory_block heap_coalesce(struct palloc_heap *heap,   const struct memory_block *blocks[], int n) {   struct memory_block ret = MEMORY_BLOCK_NONE;    const struct memory_block *b = NULL;   ret.size_idx = 0;   for (int i = 0; i &lt; n; ++i) {     if (blocks[i] == NULL)       continue;     b = b ? b : blocks[i];     ret.size_idx += blocks[i] ? blocks[i]->size_idx : 0;   }   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V547 [CWE-571] Expression &#8216;blocks[i]&#8217; is always true. heap.c 1054<\/p>\n<p>  If <i>blocks[i] == NULL<\/i>, the <i>continue<\/i> statement executes and the loop starts the next iteration. Therefore, rechecking the <i>blocks[i]<\/i>] element doesn&#8217;t make sense and the ternary operator is unnecessary. The code can be simplified:<\/p>\n<pre><code class=\"cpp\">.... for (int i = 0; i &lt; n; ++i) {   if (blocks[i] == NULL)     continue;   b = b ? b : blocks[i];   ret.size_idx += blocks[i]->size_idx; } ....<\/code><\/pre>\n<p>  <\/p>\n<h3>Suspicious use of a null pointer<\/h3>\n<p>  <\/p>\n<pre><code class=\"cpp\">void win_mmap_fini(void) {   ....   if (mt->BaseAddress != NULL)     UnmapViewOfFile(mt->BaseAddress);   size_t release_size =     (char *)mt->EndAddress - (char *)mt->BaseAddress;   void *release_addr = (char *)mt->BaseAddress + mt->FileLen;   mmap_unreserve(release_addr, release_size - mt->FileLen);   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V1004 [CWE-119] The &#8216;(char *) mt->BaseAddress&#8217; pointer was used unsafely after it was verified against nullptr. Check lines: 226, 235. win_mmap.c 235<\/p>\n<p>  The <i>mt->BaseAddress<\/i> pointer can be null, as shown by the check:<\/p>\n<pre><code class=\"cpp\">if (mt->BaseAddress != NULL)<\/code><\/pre>\n<p>  However, this pointer is already used in arithmetic operations below without checking. For example, here:<\/p>\n<pre><code class=\"cpp\">size_t release_size =   (char *)mt->EndAddress - (char *)mt->BaseAddress;<\/code><\/pre>\n<p>  Some large integer value will be obtained, which is actually equal to the value of the <i>mt->EndAddress<\/i> pointer. This may not be an error, but it looks very suspicious, and I think the code should be rechecked. The code smells as it is incomprehensible and it clearly lacks explanatory comments.<\/p>\n<h3>Short names of global variables<\/h3>\n<p>  I believe that the code smells if it contains global variables with short names. It is easy to mistype and accidentally use a global variable in some function instead of a local one. Example:<\/p>\n<pre><code class=\"cpp\">static struct critnib *c;<\/code><\/pre>\n<p>  PVS-Studio warnings for such variables:<\/p>\n<ul>\n<li>V707 Giving short names to global variables is considered to be bad practice. It is suggested to rename &#8216;ri&#8217; variable. map.c 131<\/li>\n<li>V707 Giving short names to global variables is considered to be bad practice. It is suggested to rename &#8216;c&#8217; variable. obj_critnib_mt.c 56<\/li>\n<li>V707 Giving short names to global variables is considered to be bad practice. It is suggested to rename &#8216;Id&#8217; variable. obj_list.h 68<\/li>\n<li>V707 Giving short names to global variables is considered to be bad practice. It is suggested to rename &#8216;Id&#8217; variable. obj_list.c 34<\/li>\n<\/ul>\n<p>  <\/p>\n<h2>Stranger things<\/h2>\n<p>  <\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/6d0\/05b\/d6d\/6d005bd6d867384a93c5043a84f51522.png\" alt=\"PVS-Studio: Stranger things\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/6d0\/05b\/d6d\/6d005bd6d867384a93c5043a84f51522.png\"\/><\/div>\n<p>  As for me, the <i>do_memmove<\/i> function contained the weirdest code. The analyzer issued two warnings that indicate either very serious errors, or the fact that I simply don&#8217;t understand what was meant. Since the code is very peculiar, I decided to review the warnings issued in a separate section of the article. So, the first warning is issued here.<\/p>\n<pre><code class=\"cpp\">void do_memmove(char *dst, char *src, const char *file_name,     size_t dest_off, size_t src_off, size_t bytes,     memmove_fn fn, unsigned flags, persist_fn persist) {   ....   \/* do the same using regular memmove and verify that buffers match *\/   memmove(dstshadow + dest_off, dstshadow + dest_off, bytes \/ 2);   verify_contents(file_name, 0, dstshadow, dst, bytes);   verify_contents(file_name, 1, srcshadow, src, bytes);   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V549 [CWE-688] The first argument of &#8216;memmove&#8217; function is equal to the second argument. memmove_common.c 71<\/p>\n<p>  Note that the first and second arguments of the function are the same. So the function doesn&#8217;t actually do anything. What options come to mind:<\/p>\n<ul>\n<li>The author wanted to &#171;touch&#187; the memory block. But will this happen in reality? Will the optimizing compiler remove the code that copies a block of memory to itself?<\/li>\n<li>This is some kind of a unit test for the <i>memmove<\/i> function.<\/li>\n<li>The code contains a typo.<\/li>\n<\/ul>\n<p>  And here is an equally strange fragment in the same function:<\/p>\n<pre><code class=\"cpp\">void do_memmove(char *dst, char *src, const char *file_name,     size_t dest_off, size_t src_off, size_t bytes,     memmove_fn fn, unsigned flags, persist_fn persist) {   ....   \/* do the same using regular memmove and verify that buffers match *\/   memmove(dstshadow + dest_off, srcshadow + src_off, 0);   verify_contents(file_name, 2, dstshadow, dst, bytes);   verify_contents(file_name, 3, srcshadow, src, bytes);   .... }<\/code><\/pre>\n<p>  PVS-Studio warning: V575 [CWE-628] The &#8216;memmove&#8217; function processes &#8216;0&#8217; elements. Inspect the third argument. memmove_common.c 82<\/p>\n<p>  The function transfers 0 bytes. What&#8217;s that \u2013 an error or just an extra check? A unit test? A typo?<\/p>\n<p>  For me, this code is incomprehensible and strange.<\/p>\n<h2>Why use code analyzers?<\/h2>\n<p>  It may seem that since few errors are found, the introducing an analyzer in the code development process is not justified. But the point of using static analysis tools is not to perform one-time checks, but to regularly detect errors at the code writing stage. Otherwise, these errors are detected in more expensive and slower ways (debugging, testing, user feedback, and so on). This idea is described in more detail in the article &#171;<a href=\"https:\/\/www.viva64.com\/en\/b\/0639\/\">Errors that static code analysis does not find because it is not used<\/a>&#171;, which I recommend getting acquainted with. And feel free to visit our website to <a href=\"https:\/\/www.viva64.com\/en\/pvs-studio-download\/\">download<\/a> and try PVS-Studio to scan your projects.<\/p>\n<p>  Thanks for your attention!<\/p><\/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\/515730\/\"> https:\/\/habr.com\/ru\/articles\/515730\/<\/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<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/6b7\/517\/2ab\/6b75172ab5d594657f4fa07d3c98cca1.png\" alt=\"PVS-Studio, PMDK\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/6b7\/517\/2ab\/6b75172ab5d594657f4fa07d3c98cca1.png\"\/><\/div>\n<p>  We were asked to check a collection of open source PMDK libraries for developing and debugging applications with NVRAM support by PVS-Studio. Well, why not? Moreover, this is a small project in C and C++ with a total code base size of about 170 KLOC without comments. Which means, the results review won&#8217;t take much energy and time. Let&#8217;s go.  <\/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-409185","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/409185","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=409185"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/409185\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=409185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=409185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=409185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}