Information about the software used

Configuration

This article continues the series of publications on usage of PVS-Studio in cloud systems. This time we'll look at the way the analyzer works along with GitLab CI, which is a product made by GitLab Inc. Static analyzer integration in a CI system allows detecting bugs right after the project build and is a highly effective way to reduce the cost of finding bugs.A list of our other articles on integrating into cloud CI systems: GitLab is an online service designed to manage repositories. You can use it directly in a browser on the official website by registering your account, or install and deploy it on your own server. PVS-Studio is a tool designed to detect errors and potential vulnerabilities in the source code of programs, written in C, C++, C# and Java. Works in 64-bit systems on Windows, Linux and macOS and can analyze code for 32-bit, 64-bit and embedded ARM platforms. If it's the first time you're using the analyzer to check your projects, we recommend that you read the article on how to quickly check out the most interesting PVS-Studio warnings and evaluate the tool's capabilities.The OBS project will be used to demonstrate abilities of the static analyzer in the cloud. Open Broadcaster Software is a free and open set of programs for video recording and streaming. OBS provides real-time device and source interception, scene composition, decoding, recording and broadcasting. Data is transferred mainly through the Real Time Messaging Protocol, and can be sent to any source supporting RTMP — the program has ready-made pre-installations for live broadcasting on the most popular streaming platforms.To start working with GitLab, go the website and click

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

A list of available platforms to import files:

Let's create an empty project for clarity:

Next, we need to upload our project in the created repository. Do it using the hints that appear in the window of the created project.

When you start the task, GitLab CI takes instructions from thefile. You can add it either by clicking,or simply by creating a local repository and uploading it to the site. Let's follow the first option:

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 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

- pvs-studio-analyzer credentials $PVS_NAME $PVS_KEY

Now make a minimal wrapper for the script:Download the analyzer and sendemail utility, which we will need later:Next, we will install dependencies and utilities for building OBS:Now we need to create the file with the analyzer license (By default the file PVS-Studio.lic will be created in the directory ../.config/PVS-Studio). In doing so, you don't have to specify the license file in the analyzer running parameters, it will be caught up automatically):Hereandare the names of variables the values of which we specify in settings. They will store the PVS-Studio login and license key. To set their values, follow: Settings > CI/CD > Variables.

- 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

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

Build the project, using cmake:After that, run the analyzer:will store the analysis results. The resulting file with the report is not intended for reading. In order to make it accessible for a human eye, we need the plog-converter utility. This program converts the analyzer's log into different formats. For easy reading, let's convert the log into the html format:You can export the report using artifacts , but we'll change tack and send the file with analyzer results by the email using the sendemail utility:Full .gitlab-ci.yml:Click. If we did everything right, we will see the output:To track progress, let's move to the tab

Click. We'll see the virtual machine terminal window where our configuration file runs. After a while we get a message:

Analysis results

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); } .... }

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

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

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

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); .... }

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); }

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)) { .... } .... }

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; }

if (!*modifiers && ....) Or like this: if (!combo.modifiers && ....)

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); .... }

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; } } } .... }

V636 The 'i / 6' expression was implicitly cast from 'size_t' type to 'float' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;. window-projector.cpp (330)

V636 The 'i / 2' expression was implicitly cast from 'size_t' type to 'float' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;. window-projector.cpp (334)

V636 The 'i / 2' expression was implicitly cast from 'size_t' type to 'float' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;. window-projector.cpp (340)

Conclusion

So it's time to open the html file with warnings sent by mail.Let's take a look at some warnings from the report, revealing errors in the Open Broadcaster Software project so as to get the essence of static code analysis. Since the main purpose of the article is to describe principles of PVS-Studio and GitLab CI/CD interaction, only several nontrivial examples have been picked over. We are ready to give the authors of the project a temporary license and, if they wish, they are welcome to perform a more thorough project analysis. In addition, they can use one of the ways to get a free PVS-Studio license Also everyone can get a trial key to explore the PVS-Studio capabilities and check their projects.So, let's pay attention to some examples of found errors in Open Broadcaster Software. V547 Expression 'back_size' is allways true. circlebuf.h (138)The lineis definitely worth taking a close look at. If the condition is true, thevariable, defined in the line below, will always be greater than zero, as here we deal with subtraction of the notoriously smaller value from the greater one. In the end, the condition, which is two lines below, will always be. The redundant condition is not that harmless when it's followed by the code, changing data. 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)Confused operations over 32-bit and 64-bit types look suspicious here. First, the programmer evaluates the mask, using 32-bit types (expression), after that it implicitly expands into the 64-bit type in the expression. Most likely, when evaluating the mask, usage of 64-bit types was also required.Correct code version:Or:By the way, the copy-paste version of this block is below, the analyzer also issued the warning for it: 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) V761 Four identical blocks of text were found. 'obs-audio-controls.c' (353)Four identical blocks. Almost in every case such code indicates aerror. Most likely, these functions must have been called with different arguments. Even if not, this code looks strange. A good solution would be to write the block only once and wrap it in a loop: V560 A part of conditional expression is always false: '!modifiers'. obs-hotkey.c (662)Definition of thefunction:As we can see,is a pointer, initialized by the address of thefield of thestructure. Since its value doesn't change until the check, it will remain non-null. Moreover, after the initialization before the check the pointer is used when calling thefunction, where it gets dereferenced. Accordingly, thecheck is pointless, as due to theoperator we'll always getwhen evaluating the logical expression. I think the programmer wanted to check an integer value by the address that is stored in thepointer, but forgot to dereference this pointer.So it seems to me that the check should be as follows: V575 The potential null pointer is passed into 'strncpy' function. Inspect the first argument. Check lines: 2904, 2903. rtmp.c (2904)Most often such code is unsafe, as it ignores thatcan return a null pointer. Ifreturns, undefined behavior will occur in this case, as the first argument of thefunction will have thevalue.For more information on why it's important to check the return value of thefunction, check out the relevant article Guess which cases contain incorrect calculations:Analyzer warnings:Here's the right answer: in the ones, whereisn't cast to float. The analyzer shows us fragments with integer division. Such code might work not in the way the programmer had hoped.As we can see, the PVS-Studio analyzer integration in your project on GitLab is a quite simple process. To do this, you just have to write only one configuration file and place it in your cloud repository. Due to the fact that GitLab has its own integrated virtual machine, we don't even need to spend a lot of time configuring the CI system. Code checking will let you find problems right after the build. This helps to eliminate problems at the stage where their complexity and cost are small as yet.