PVS-Studio goes to the clouds: GitLab CI / CD





Figure 2






This article is a continuation of a series of publications on the use of PVS-Studio in cloud systems. This time we will look at the analyzer in conjunction with GitLab CI, a product from GitLab Inc. The integration of a static analyzer in the CI system allows you to identify bugs immediately after the build phase of the project and is a very effective way to reduce the cost of detecting errors.



List of our other articles on integration into cloud CI systems:





Information about the software used



GitLab is an online repository management service. It can be used directly in the browser on the official website by registering an account, or installed and deployed on your own server.



PVS-Studio is a static code analysis tool designed to detect errors and potential vulnerabilities in programs written in C, C ++, C # and Java. It works on 64-bit systems on Windows, Linux and macOS and can analyze code designed for 32-bit, 64-bit and embedded ARM platforms. If this is your first time trying static code analysis to test your projects, we recommend that you read the article on how to quickly see the most interesting warnings from PVS-Studio and evaluate the capabilities of this tool.



The OBS project will be used to demonstrate the operation of a static analyzer in the cloud. Open Broadcaster Software is a free and open source suite of video recording and streaming software. OBS provides the ability to intercept from devices and sources in real time, scene composition, decoding, recording and broadcasting. Data transmission is carried out mainly through the Real Time Messaging Protocol, and data can be transferred to any source that supports RTMP — the program has ready-made presets for direct broadcasting to the most popular streaming platforms.



Customization



To get started with GitLab, go to the site and click the Register button:







Figure 6






You can register by linking the accounts of such services as: GitHub, Twitter, Google, BitBucket, Saleforce or simply by filling out the form that opens. After authorization, GitLab meets us with a proposal to create a project:







Figure 7






List of platforms from which you can import:







Figure 33










For clarity, create an empty project:







Figure 17










Next, we need to upload our project to the created repository. This is done using the prompts that appear in the window of the created project.







Picture 1










When the task starts, GitLab CI takes instructions from the .gitlab-ci.yml file . You can add it either by clicking on the Set up CI / CD button, or simply creating it in the local repository and uploading it to the site. We will use the first option:





Figure 21












Let's make a minimal wrapper for the script:



image: debian job: script:
      
      





Download the analyzer and sendemail utility, which we will need in the future:



 - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail
      
      





Next, install the dependencies and utilities for building OBS:



 - apt-get -y install build-essential cmake make pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev libio-socket-ssl-perl libnet-ssleay-perl ca-certificates
      
      





Now we need to create a file with the analyzer license. By default, the PVS-Studio.lic file will be created in the ../.config/PVS-Studio directory. In this case, the license file can be omitted from the analyzer launch parameters, it will be picked up automatically:



 - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY
      
      





Here PVS_NAME and PVS_KEY are the names of the variables whose values ​​we specify in the settings. They will store the PVS-Studio login and license key. To set their values, go to: Settings> CI / CD> Variables.







Figure 23







Build the project using cmake:



 - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4
      
      





Then run the analyzer:



 - pvs-studio-analyzer analyze -o PVS-Studio.log
      
      





PVS-Studio.log will store the analysis results. The resulting report file is not intended for reading, and to bring it into a view accessible to the human eye, we need the plog-converter utility. This program converts the analyzer log to various formats. For readability, we will convert to html format:



 - plog-converter -t html PVS-Studio.log -o PVS-Studio.html
      
      





The report can be uploaded using artifacts , but we will use an alternative method and send the file with the results of the analyzer to the mail using the sendemail utility:



 - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html
      
      





Full .gitlab-ci.yml:



 image: debian job: script: - apt-get update && apt-get -y install wget gnupg - wget -O - https://files.viva64.com/etc/pubkey.txt | apt-key add - - wget -O /etc/apt/sources.list.d/viva64.list https://files.viva64.com/etc/viva64.list - apt-get update && apt-get -y install pvs-studio sendemail - apt-get -y install build-essential cmake pkg-config libx11-dev libgl1-mesa-dev libpulse-dev libxcomposite-dev libxinerama-dev libv4l-dev libudev-dev libfreetype6-dev libfontconfig-dev qtbase5-dev libqt5x11extras5-dev libx264-dev libxcb-xinerama0-dev libxcb-shm0-dev libjack-jackd2-dev libcurl4-openssl-dev libavcodec-dev libqt5svg5 libavfilter-dev libavdevice-dev libsdl2-dev ffmpeg qt5-default qtscript5-dev libssl-dev qttools5-dev qttools5-dev-tools qtmultimedia5-dev libqt5svg5-dev libqt5webkit5-dev libasound2 libxmu-dev libxi-dev freeglut3-dev libasound2-dev libjack-jackd2-dev libxrandr-dev libqt5xmlpatterns5-dev libqt5xmlpatterns5 coccinelle parallel libapparmor-dev libcap-dev libseccomp-dev python3-dev python3-setuptools docbook2x libgnutls28-dev libselinux1-dev linux-libc-dev libtool autotools-dev make libio-socket-ssl-perl libnet-ssleay-perl ca-certificates - pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY - cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On /builds/Stolyarrrov/obscheck/ - make -j4 - pvs-studio-analyzer analyze -o PVS-Studio.log - plog-converter -t html PVS-Studio.log -o PVS-Studio.html - sendemail -t $MAIL_TO -u "PVS-Studio report, commit:GITLAB_COMMIT" -m "PVS-Studio report, commit:GITLAB_COMMIT" -s $GMAIL_PORT -o tls=auto -f $MAIL_FROM -xu $MAIL_FROM -xp $MAIL_FROM_PASS -a PVS-Studio.log PVS-Studio.html
      
      





Click on the commit changes button. If we did everything correctly, we will see the inscription: This GitLab CI configuration is valid. To track progress, go to the CI / CD> Pipelines tab .







Figure 5







Click on running . We will see the terminal window of the virtual machine on which our configuration file is running. After some time, we get the message: job succeeded.







Figure 29







So, time to go to the mail and open the html file with warnings.



Validation Results



Now, to demonstrate the essence of static code analysis, let's look at some warnings from the report that indicate errors in the Open Broadcaster Software project. The purpose of the article is to describe the principles of interaction between PVS-Studio and GitLab CI / CD, so only some interesting code fragments with errors were written out. We are ready to grant a temporary license to the authors of the project, and if they wish, they can conduct a more thorough analysis of the project. Or they can use one of the options for free licensing of PVS-Studio.



Everyone can also independently receive a trial key for exploring the capabilities of PVS-Studio and checking their projects.



So, let's look at some examples of errors found in Open Broadcaster Software.



Warning N1



V547 Expression 'back_size' is allways true. circlebuf.h (138)



 struct circlebuf { .... size_t capacity; }; static inline void circlebuf_place(struct circlebuf *cb, size_t position,....,const void *data, size_t size) { .... size_t data_end_pos; data_end_pos = position + size; if (data_end_pos > cb->capacity) { size_t back_size = data_end_pos - cb->capacity; if (back_size) { memcpy((uint8_t *)cb->data + position, data, loop_size); } .... }
      
      





Pay attention to the line: if (data_end_pos> cb-> capacity) , the fulfillment of the condition will indicate that the variable back_size , defined in the line below, will always be greater than zero, since the obviously smaller from the obviously larger is subtracted, which means that the condition is another line below, will always be true . An excess condition is not so harmless when under it is a code that modifies data.



Warnings N2, N3



V629 Consider inspecting the '1 << indent' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. profiler.c (610)



 static void profile_print_entry(uint64_t active, unsigned indent, ....) { .... active &= (1 << indent) - 1; .... }
      
      





The mix of operations on 32-bit and 64-bit types looks suspicious here. The mask is first computed using 32-bit types (expression (1 << indent) - 1 ), and then it is implicitly expanded to the 64-bit type in the expression active & = .... Most likely, when calculating the mask, the use of 64-bit types was also assumed.



The correct version of the code:



 active &= ((uint64_t)(1) << indent) - 1;
      
      





Or:



 active &= (1ull << indent) - 1;
      
      





Also, copy-paste of this code block is below, which the analyzer also issued a warning about: V629 Consider inspecting the '1 << indent' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. profiler.c (719)



Warning N4



V761 Four identical blocks of text were found. 'obs-audio-controls.c' (353)



 static float get_true_peak(....) { .... peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); .... }
      
      





Four identical blocks. Almost always, such code indicates a copy-paste error. Most likely these functions should have been called with different arguments. Even if not, this code looks strange. A good solution would be to write a block once and wrap it in a loop:



 for(size_t i = 0; i < 3; i++) { peak = _mm_max_ps(peak, abs_ps(intrp_samples)); SHIFT_RIGHT_2PS(new_work, work); VECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3); }
      
      





Warning N5



V560 A part of conditional expression is always false: '! Modifiers'. obs-hotkey.c (662)



 typedef struct obs_key_combination obs_key_combination_t; struct obs_key_combination { uint32_t modifiers; obs_key_t key; }; static inline void load_binding(....) { obs_key_combination_t combo = {0}; uint32_t *modifiers = &combo.modifiers; load_modifier(modifiers, data, "shift", INTERACT_SHIFT_KEY); load_modifier(modifiers, data, "control", INTERACT_CONTROL_KEY); load_modifier(modifiers, data, "alt", INTERACT_ALT_KEY); load_modifier(modifiers, data, "command", INTERACT_COMMAND_KEY); if (!modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE)) { .... } .... }
      
      





The definition of the load_modifier function:



 static inline void load_modifier(uint32_t *modifiers, obs_data_t *data, const char *name, uint32_t flag) { if (obs_data_get_bool(data, name)) *modifiers |= flag; }
      
      





As we can see, modifiers is a pointer that is initialized with the address of the modifiers field of the combo structure. Since its value does not change to the place of verification, it will remain nonzero. Moreover - between the place of initialization and verification, the pointer is used when calling the load_modifier function, where it is dereferenced. Accordingly, checking ! Modifiers does not make sense, since as a result of the && operator, we will always get false when evaluating a logical expression. I think that the programmer wanted to check the integer value located at the address that is stored in the modifiers pointer, but forgot to dereference this pointer.



Those. It seems to me that the check should be like this:



 if (!*modifiers && ....)  : if (!combo.modifiers && ....)
      
      





Warning N6



V575 The potential null pointer is passed into 'strncpy' function. Inspect the first argument. Check lines: 2904, 2903. rtmp.c (2904)



 static int PublisherAuth(....) { .... ptr = malloc(r->Link.app.av_len + pubToken.av_len); strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); .... }
      
      





Often, this code is unsafe because it does not take into account that malloc can return a null pointer. If malloc returns NULL , an undefined behavior will occur in this case, since the first argument to strncpy will be NULL .



You can read more about why it is important to check the return value of the malloc function in the corresponding article.



Warnings N7, N8, N9



Let's try to guess in which of the cases the wrong calculations may occur:



 class OBSProjector : public OBSQTDisplay { .... float sourceX, sourceY, ....; .... } .... void OBSProjector::OBSRenderMultiview(....) { OBSProjector *window = (OBSProjector *)data; .... auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX += window->scenesCX; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2) * window->scenesCY; if (i % 2 != 0) { window->sourceX = window->scenesCX; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default:// MultiviewLayout::HORIZONTAL_TOP_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = window->pvwprgCY; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->pvwprgCY + window->scenesCY; } } } .... }
      
      





Analyzer Warnings:





The correct answer: in those in which i is not cast to float . In the expressions the analyzer shows us, integer division occurs. This code may not work exactly as the programmer expected.



Conclusion



As we can see, integrating the PVS-Studio static analyzer into your GitLab project is quite simple. To do this, just write one configuration file and put it in your cloud repository. Due to the fact that GitLab has its own integrated virtual machine, we do not even need to spend a lot of time to configure the CI system. Checking the code will allow you to identify problems immediately after assembly, which will help to eliminate them when the complexity and cost of edits are still small.











If you want to share this article with an English-speaking audience, then please use the link to the translation: Vladislav Stolyarov. PVS-Studio in the Clouds: GitLab CI / CD .



All Articles