{"id":380706,"date":"2024-06-29T03:15:14","date_gmt":"2024-06-29T03:15:14","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=380706"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=380706","title":{"rendered":"<span>MuditaOS: Will your alarm clock go off? Part I<\/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-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>Operating systems are a kind of software where code quality is critical. This time the PVS-Studio analyzer checked MuditaOS. So let&#8217;s take a look at what the static analyzer found in this open-source OS.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/70a\/2df\/124\/70a2df12440004a9e44bbb5e792f8213.png\" width=\"780\" height=\"440\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/70a\/2df\/124\/70a2df12440004a9e44bbb5e792f8213.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>About the project<\/h3>\n<p><a href=\"https:\/\/mudita.com\/\">MuditaOS<\/a> is an operating system based on FreeRTOS that PVS-Studio checked a while ago. What did we find? Check out <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/cpp\/0684\/\">this article<\/a>! MuditaOS runs on Mudita devices that include a phone, alarm clocks, and a watch. The source code is in C and C++. So. Why don&#8217;t we take a look? How good are these alarm clocks, really? \ud83d\ude42<\/p>\n<p>We followed the <a href=\"https:\/\/github.com\/mudita\/MuditaOS\/blob\/master\/doc\/quickstart.md\">instructions<\/a> from the official repository and built the project under Ubuntu 20.04. We checked the debug version for the Mudita Bell alarm clock. At the end of 2021 the alarm clock cost <em>$60<\/em>. This is what it looked like:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b63\/a46\/63c\/b63a4663c446aac2488bf5387320e301.png\" width=\"580\" height=\"316\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b63\/a46\/63c\/b63a4663c446aac2488bf5387320e301.png\"\/><figcaption><\/figcaption><\/figure>\n<p>Since the project is regularly updated, I froze it in version <a href=\"https:\/\/github.com\/mudita\/MuditaOS\/tree\/8cc1f779db7824a665240c5a89f1590a5831502c\">8cc1f77<\/a>.<\/p>\n<h3>The analyzer&#8217;s warnings<\/h3>\n<h4>Warnings N1\u2013N3<\/h4>\n<p>Before moving on to errors, I&#8217;ll tell you about one amusing case. I&#8217;ve recently given a lecture at Tula State University about undefined behavior. Here&#8217;s what I wrote on the <em>bio<\/em> slide:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/657\/14b\/b7a\/65714bb7a391a97c74571e7cd08b0be2.PNG\" width=\"580\" height=\"327\"\/><figcaption><\/figcaption><\/figure>\n<p>This requires a bit of a clarification. During code analysis, the PVS-Studio analyzer builds an abstract syntax tree that represents the project&#8217;s code. This is one of the intermediate stages of analysis. The tree&#8217;s nodes represent various language constructs. The latter ones are positioned according to the inheritance hierarchy. From node to node, the language constructs are converted through casts.<\/p>\n<p>When I was just starting out at PVS-Studio, I crashed the analyzer several times (during trial runs), because I was too sure that I knew the type of the node to which I was casting the base type node.<\/p>\n<p>Today I&#8217;ll prove to you that, same as me, MuditaOS developers do not like checking type casts&#8217; results too much. Let&#8217;s see what the analyzer warns about:<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v595\/\">V595<\/a> [CERT-EXP12-C] The &#8216;result&#8217; pointer was utilized before it was verified against nullptr. Check lines: 81, 82. AudioModel.cpp 81<\/p>\n<pre><code class=\"cpp\">void AudioModel::play(....) {   ....   auto cb = [_callback = callback, this](auto response)              {               auto result = dynamic_cast                             &lt;service::AudioStartPlaybackResponse *>(response);               lastPlayedToken = result->token;               if (result == nullptr)                {                 ....               }               ....             };   .... } <\/code><\/pre>\n<p>In this code fragment, the developer uses <em>dynamic_cast<\/em> for type casting. This operation&#8217;s result is a potentially null pointer that is later dereferenced. Then, this the pointer is checked for <em>nullptr<\/em>.<\/p>\n<p>Fixing this code is easy. First, check the <em>result<\/em> pointer for null. Then use it.<\/p>\n<p>Below are two cases that are even more interesting:<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v757\/\">V757<\/a> [CERT-EXP12-C] It is possible that an incorrect variable is compared with nullptr after type conversion using &#8216;dynamic_cast&#8217;. Check lines: 214, 214. CallLogDetailsWindow.cpp 214<\/p>\n<pre><code class=\"cpp\">void CallLogDetailsWindow::onBeforeShow(...., SwitchData *data) {   ....   if (auto switchData = dynamic_cast                         &lt;calllog::CallLogSwitchData *>(data); data != nullptr)    {     ....   }   .... } <\/code><\/pre>\n<p>Here developer uses <em>dynamic_cast<\/em> to cast the pointer to the base class, to the pointer to the derivative. Then the pointer being cast is checked for <em>nullptr<\/em>. However, most likely, the developer intended to check the cast&#8217;s result for <em>nullptr<\/em>. In case this is indeed a typo, one can fix the code as follows:<\/p>\n<pre><code class=\"cpp\">void CallLogDetailsWindow::onBeforeShow(...., SwitchData *data) {   ....   if (auto switchData = dynamic_cast&lt;calllog::CallLogSwitchData *>(data))    {     ....   }   .... } <\/code><\/pre>\n<p>It&#8217;s possible not everyone likes this fix, but we consider it short and convenient \u2014 we initialize and check the pointer in one operation \u2014 which is why we use the approach everywhere.<\/p>\n<p>Note. This is different from the case when an existing variable is assigned inside a condition. The code below is considered poor practice:<\/p>\n<pre><code class=\"cpp\">int x = ...; if (x = foo()) <\/code><\/pre>\n<p>It&#8217;s not clear whether they attempted to write a comparison, but made a typo or whether they truly intended to assign and check the variable simultaneously. Most compilers and analyzers <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v559\/\">warn<\/a> about such code \u2014and rightfully so. The code is dangerous and unclear. However, it&#8217;s a completely different matter when someone creates a new variable as is shown in the example. There someone attempted to create a new variable and initialize it with a specific value. You wouldn&#8217;t be able to perform the == operation there, no matter how bad you might want it.<\/p>\n<p>Let&#8217;s get back to the project&#8217;s code. Below is one similar case:<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v757\/\">V757<\/a> [CERT-EXP12-C] It is possible that an incorrect variable is compared with nullptr after type conversion using &#8216;dynamic_cast&#8217;. Check lines: 47, 47. PhoneNameWindow.cpp 47<\/p>\n<pre><code class=\"cpp\">void PhoneNameWindow::onBeforeShow(ShowMode \/*mode*\/, SwitchData *data) {   if (const auto newData = dynamic_cast&lt;PhoneNameData *>(data);                                                              data != nullptr)    {     ....   } } <\/code><\/pre>\n<p>The correct code looks like this:<\/p>\n<pre><code class=\"cpp\">void PhoneNameWindow::onBeforeShow(ShowMode \/*mode*\/, SwitchData *data) {   if (const auto newData = dynamic_cast&lt;PhoneNameData *>(data))    {     ....   } } <\/code><\/pre>\n<p>Note that simplifying such checks is one of our code refactoring recommendations we covered in <a href=\"https:\/\/pvs-studio.com\/en\/blog\/video\/10513\/\">this video.<\/a> Do take a look if you haven&#8217;t already! It&#8217;s short and you may learn something new \ud83d\ude42<\/p>\n<h4>Warning N4<\/h4>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v522\/\">V522<\/a> [CERT-EXP34-C] Dereferencing of the null pointer &#8216;document&#8217; might take place. TextBlockCursor.cpp 332<\/p>\n<pre><code class=\"cpp\">auto BlockCursor::begin() -> std::list&lt;TextBlock>::iterator {   return document == nullptr              ? document->blocks.end() : document->blocks.begin(); } <\/code><\/pre>\n<p>This code fragment deserves its very own facepalm. Let&#8217;s figure out what happens here. The developer explicitly checks the <em>document<\/em> pointer for <em>nullptr<\/em>. Then the pointer is dereferenced in both branches of the ternary operator. The code is correct only if the developer aimed to crash the program.<\/p>\n<h4>Warning N5<\/h4>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v517\/\">V517<\/a> [CERT-MSC01-C] The use of &#8216;if (A) {&#8230;} else if (A) {&#8230;}&#8217; pattern was detected. There is a probability of logical error presence. Check lines: 1053, 1056. avdtp_util.c 1053<\/p>\n<pre><code class=\"cpp\">static uint16_t avdtp_signaling_setup_media_codec_mpeg_audio_config_event(....) {   uint8_t channel_mode_bitmap = ....;   ....   if (....)   {     ....   }   else if (channel_mode_bitmap &amp; 0x02)   {     num_channels = 2;     channel_mode = AVDTP_CHANNEL_MODE_STEREO;   }   else if (channel_mode_bitmap &amp; 0x02)   {     num_channels = 2;     channel_mode = AVDTP_CHANNEL_MODE_JOINT_STEREO;   }   .... } <\/code><\/pre>\n<p>Here we can see classic copy-pasted code. There are two ways to understand and fix this code: either the second branch should contain a different check, or the second check is redundant and needs to be removed. Since the two branches contain different logic, I assume the first variant applies here. In any case, I recommend MuditaOS developers to take a look at this code snippet.<\/p>\n<h4>Warnings N6, N7<\/h4>\n<ul>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v571\/\">V571<\/a> Recurring check. The &#8216;if (activeInput)&#8217; condition was already verified in line 249. ServiceAudio.cpp 250<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v547\/\">V547<\/a> Expression &#8216;activeInput&#8217; is always true. ServiceAudio.cpp 250<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"cpp\">std::optional&lt;AudioMux::Input *> AudioMux::GetActiveInput();  ....  auto Audio::handleSetVolume(....) -> std::unique_ptr&lt;AudioResponseMessage> {   ....   if (const auto activeInput = audioMux.GetActiveInput(); activeInput)    {     if (activeInput)      {       retCode = activeInput.value()->audio->SetOutputVolume(clampedValue);     }   }   .... } <\/code><\/pre>\n<p>Let&#8217;s investigate. The <em>activeinput<\/em> type is an <em>std::optional<\/em> entity from the pointer to <em>AudioMax::input<\/em>. The nested <em>if<\/em> statement contains the <em>value member function call.<\/em> The function is guaranteed to return the pointer and will not throw an exception. After, the result is dereferenced.<\/p>\n<p>However, the function may return either a valid \u2014 or a null pointer. The plan for the nested <em>if<\/em> statement was probably to check this pointer. Hm, I also like wrapping pointers and boolean values in <em>std::optional<\/em>! And then going through the same grief each time :).<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/061\/be3\/ab1\/061be3ab1c7e522f1183d7be59b1fd7b.png\" width=\"580\" height=\"500\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/061\/be3\/ab1\/061be3ab1c7e522f1183d7be59b1fd7b.png\"\/><figcaption><\/figcaption><\/figure>\n<p>The fixed code:<\/p>\n<pre><code class=\"cpp\">std::optional&lt;AudioMux::Input *> AudioMux::GetActiveInput();  ....  auto Audio::handleSetVolume(....) -> std::unique_ptr&lt;AudioResponseMessage> {   ....   if (const auto activeInput = audioMux.GetActiveInput(); activeInput)    {     if (*activeInput)      {       retCode = (*activeInput)->audio->SetOutputVolume(clampedValue);     }   }   .... } <\/code><\/pre>\n<h4>Warning N8\u2013N11<\/h4>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v668\/\">V668<\/a> [CERT-MEM52-CPP] There is no sense in testing the &#8216;pcBuffer&#8217; pointer against null, as the memory was allocated using the &#8216;new&#8217; operator. The exception will be generated in the case of memory allocation error. syscalls_stdio.cpp 384<\/p>\n<pre><code class=\"cpp\">int _iosys_fprintf(FILE *__restrict __stream,                    const char *__restrict __format, ...) {   constexpr auto buf_len = 4096;   char *pcBuffer;   ....   pcBuffer = new char[buf_len];   if (pcBuffer == NULL)    {     ....   } } <\/code><\/pre>\n<p>Here the pointer value, that the <em>new<\/em> operator (which is not overloaded, as far as I can tell) returns*,* is compared to <em>NULL<\/em>. However, if the <em>new<\/em> operator fails to allocate memory, then, according to the language standard, the <em>std::bad_alloc()<\/em> exception is generated. Consequently, checking the pointer for null makes no sense.<\/p>\n<p>Even less so in the code of an operating system that functions in real time. Most likely, in cases when memory cannot be allocated, the program will crash and the code that follows will be simply unreachable.<\/p>\n<p>The check may take place if the <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/memory\/new\/nothrow\"><em>nothrow<\/em><\/a> overload of <em>new<\/em> is employed:<\/p>\n<pre><code class=\"cpp\">int _iosys_fprintf(FILE *__restrict __stream,                    const char *__restrict __format, ...) {   constexpr auto buf_len = 4096;   char *pcBuffer;   ....   pcBuffer = new (std::nothrow) char[buf_len];   if (pcBuffer == NULL)    {     ....   } } <\/code><\/pre>\n<p>The analyzer found several more such cases.<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v668\/\">V668<\/a> [CERT-MEM52-CPP] There is no sense in testing the &#8216;fontData&#8217; pointer against null, as the memory was allocated using the &#8216;new&#8217; operator. The exception will be generated in the case of memory allocation error. FontManager.cpp 56<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v668\/\">V668<\/a> [CERT-MEM52-CPP] There is no sense in testing the &#8216;data&#8217; pointer against null, as the memory was allocated using the &#8216;new&#8217; operator. The exception will be generated in the case of memory allocation error. ImageManager.cpp 85<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v668\/\">V668<\/a> [CERT-MEM52-CPP] There is no sense in testing the &#8216;data&#8217; pointer against null, as the memory was allocated using the &#8216;new&#8217; operator. The exception will be generated in the case of memory allocation error. ImageManager.cpp 131<\/p>\n<\/li>\n<\/ul>\n<h4>Warning N12<\/h4>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v509\/\">V509<\/a> [CERT-DCL57-CPP] The noexcept function &#8216;=&#8217; calls function &#8216;setName&#8217; which can potentially throw an exception. Consider wrapping it in a try..catch block. Device.cpp 48<\/p>\n<pre><code class=\"cpp\">struct Device {   static constexpr auto NameBufferSize = 240;   ....   void setName(const std::string &amp;name)   {     if (name.size() > NameBufferSize)      {         throw std::runtime_error(\"Requested name is bigger than buffer                                    size\");     }     strcpy(this->name.data(), name.c_str());   }   .... }  ....  Devicei &amp;Devicei::operator=(Devicei &amp;&amp;d) noexcept {   setName(d.name.data()); } <\/code><\/pre>\n<p>Here the analyzer detected that a function, marked as <em>noexcept<\/em>, calls a function that throws an exception. If an exception arises from the nothrow function&#8217;s body, the nothrow function calls <em>std::terminate<\/em>, and the program crashes.<\/p>\n<p>It could make sense to wrap the <em>setName<\/em> function in the function-try block and process the exceptional situation there \u2014 or one could use something else instead of generating the exception.<\/p>\n<h4>Warnings N13\u2013N18<\/h4>\n<p>The analyzer found many code fragments that contain meaningless checks. Let&#8217;s examine a few of them, and leave the rest to the developers:<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v547\/\">V547<\/a> Expression &#8216;snoozeCount == 0&#8217; is always true. NotificationProvider.cpp 117<\/p>\n<pre><code class=\"cpp\">void NotificationProvider::handleSnooze(unsigned snoozeCount) {   if (snoozeCount > 0)    {     notifications[NotificationType::AlarmSnooze] =        std::make_shared&lt;notifications::AlarmSnoozeNotification>(snoozeCount);   }   else if (snoozeCount == 0)   {     notifications.erase(NotificationType::AlarmSnooze);   }    send(); } <\/code><\/pre>\n<p>As is obvious from the code, the <em>snoozeCount<\/em> variable is of an unsigned type \u2014 and, consequently, cannot be less than zero. So the second check is redundant. The code becomes more concise if we replace <em>else if<\/em> with the conditionless <em>else<\/em>:<\/p>\n<pre><code class=\"cpp\">void NotificationProvider::handleSnooze(unsigned snoozeCount) {   if (snoozeCount > 0)    {     notifications[NotificationType::AlarmSnooze] =        std::make_shared&lt;notifications::AlarmSnoozeNotification>(snoozeCount);   }   else   {     notifications.erase(NotificationType::AlarmSnooze);   }    send(); } <\/code><\/pre>\n<p>The analyzer also issued a warning for this code fragment:<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v547\/\">V547<\/a> Expression &#8216;currentState == ButtonState::Off&#8217; is always true. ButtonOnOff.cpp 33<\/p>\n<pre><code class=\"cpp\">enum class ButtonState : bool {   Off,   On }; .... void ButtonOnOff::switchState(const ButtonState newButtonState) {   currentState = newButtonState;   if (currentState == ButtonState::On)    {     ....   }   else if (currentState == ButtonState::Off)    {     ....   } } <\/code><\/pre>\n<p>This warning is interesting, because normally developers could just suppress it. Let&#8217;s figure out what happens here: we have an <em>enum<\/em> with the underlying <em>bool<\/em> type and two states that we are checking.<\/p>\n<p>We all know that developers often expand enumerations and add new values. With time, this enumeration could obtain more states and the total could exceed two. Then the analyzer would have stopped warning about this code fragment.<\/p>\n<p>However, I would like to draw your attention to the fact that this is a button&#8217;s state. It can be clicked \u2014 or not \u2014 but I doubt that the authors are planning to invent a Schroedinger button any time soon and add a third state. You can use the same approach to fix this code \u2014 replace <em>else if<\/em> with the unconditional <em>else<\/em>.<\/p>\n<pre><code class=\"cpp\">void ButtonOnOff::switchState(const ButtonState newButtonState) {   currentState = newButtonState;   if (currentState == ButtonState::On)    {     ....   }   else   {     ....   } } <\/code><\/pre>\n<p>Here are a few more <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v547\/\">V547<\/a> that are worth paying attention to:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v547\/\">V547<\/a> Expression &#8216;status != 0x00&#8217; is always false. AVRCP.cpp 68<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v547\/\">V547<\/a> Expression &#8216;stream_endpoint->close_stream == 1&#8217; is always false. avdtp.c 1223<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v547\/\">V547<\/a> Expression &#8216;stream_endpoint->abort_stream == 1&#8217; is always false. avdtp.c 1256<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v547\/\">V547<\/a> Expression &#8216;what == info_type::start_sector&#8217; is always true. disk_manager.cpp 340<\/p>\n<\/li>\n<\/ul>\n<h4>Warning N19<\/h4>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v609\/\">V609<\/a> [CERT-EXP37-C] Divide by zero. The &#8216;qfilter_CalculateCoeffs&#8217; function processes value &#8216;0&#8217;. Inspect the third argument. Check lines: &#8216;Equalizer.cpp:26&#8217;, &#8216;unittest_equalizer.cpp:91&#8217;. Equalizer.cpp 26<\/p>\n<pre><code class=\"cpp\">\/\/ Equalizer.cpp QFilterCoefficients qfilter_CalculateCoeffs(         FilterType filter, float frequency, uint32_t samplerate, float Q,          float gain) {   constexpr auto qMinValue         = .1f;   constexpr auto qMaxValue         = 10.f;   constexpr auto frequencyMinValue = 0.f;    if (frequency &lt; frequencyMinValue &amp;&amp; filter != FilterType::FilterNone)    {     throw std::invalid_argument(\"Negative frequency provided\");   }   if ((Q &lt; qMinValue || Q > qMaxValue) &amp;&amp; filter != FilterType::FilterNone)    {     throw std::invalid_argument(\"Q out of range\");   }   ....   float omega    = 2 * M_PI * frequency \/ samplerate;   .... } .... \/\/ unittest_equalizer.cpp const auto filterNone = qfilter_CalculateCoeffs(FilterType::FilterNone,                                                 0, 0, 0, 0); <\/code><\/pre>\n<p>Yes, a unit test was what triggered the analyzer here. However, I think that this case is interesting and could be a good example. This is a very strange operation and our intermodular analysis detected it.<\/p>\n<p>By the way, intermodular analysis is a large new feature in the PVS-Studio analyzer. For more information on this feature, see <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/cpp\/0851\/\">this article<\/a>.<\/p>\n<p>But let&#8217;s get back to the warning. Here the developer who wrote the test most likely did not look inside the <em>qfilter_CalculateCoeffs<\/em> function. The result of dividing by <em>0<\/em> is the following:<\/p>\n<ul>\n<li>\n<p>for integers \u2014 undefined behavior, after which there&#8217;s no point testing anything, since anything can happen;<\/p>\n<\/li>\n<li>\n<p>for real numbers \u2014 the <em>\u00b1Inf<\/em> value if the type in question supports arithmetic with floating point numbers, according to the <em>IEC 559<\/em> \/ <em>IEEE 754<\/em>, otherwise it&#8217;s undefined behavior, same as for integers.<\/p>\n<\/li>\n<\/ul>\n<p>Here we have a floating point number. This is why when dividing by <em>0<\/em>, we will most likely get infinity. The result probably would not make the code author happy. Click <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/operator_arithmetic\">here<\/a> to learn more on this topic.<\/p>\n<p>As a result, we see that the test contains clearly dangerous code that prevents the correct testing of the product.<\/p>\n<h4>Warnings N20\u2013N21<\/h4>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v617\/\">V617<\/a> Consider inspecting the condition. The &#8216;purefs::fs::inotify_flags::close_write&#8217; argument of the &#8216;|&#8217; bitwise operation contains a non-zero value. InotifyHandler.cpp 76<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v617\/\">V617<\/a> Consider inspecting the condition. The &#8216;purefs::fs::inotify_flags::del&#8217; argument of the &#8216;|&#8217; bitwise operation contains a non-zero value. InotifyHandler.cpp 79<\/p>\n<pre><code class=\"cpp\">namespace purefs::fs {   enum class inotify_flags : unsigned   {     attrib        = 0x01,     close_write   = 0x02,     close_nowrite = 0x04,     del           = 0x08,     move_src      = 0x10,     move_dst      = 0x20,     open          = 0x40,     dmodify       = 0x80,   };   .... }  sys::MessagePointer InotifyHandler::handleInotifyMessage                                    (purefs::fs::message::inotify *inotify) {   ....   if (inotify->flags        &amp;&amp;   (purefs::fs::inotify_flags::close_write            | purefs::fs::inotify_flags::move_dst))    {     ....   }   else if (inotify->flags             &amp;&amp;   ( purefs::fs::inotify_flags::del                  | purefs::fs::inotify_flags::move_src))    {     ....   }   .... } <\/code><\/pre>\n<p>This case looks like a classic pattern when a developer wants to make sure that one of the flags is set in <em>inotify->flags<\/em>. In the first case it&#8217;s <em>close_write<\/em> or <em>move_dst<\/em>, in the second cast it&#8217;s <em>del<\/em> or <em>move_src<\/em> consequently.<\/p>\n<p>Let&#8217;s think about how we can make this happen. To do this, first, we need to join constants through the use of the <em>|<\/em> operation \u2014 that&#8217;s exactly what the developer did. Then make sure that one of them is set in <em>flags<\/em> through the <em>&amp;<\/em> operation.<\/p>\n<p>This code fragment looks strange and is hardly correct. The &amp;&amp; operator&#8217;s second operand is always true.<\/p>\n<p>Most likely, the developer mixed up the logical <em>&amp;&amp;<\/em> and the bitwise <em>&amp;<\/em>. The correct code is as follows:<\/p>\n<pre><code class=\"cpp\">sys::MessagePointer InotifyHandler::handleInotifyMessage                                    (purefs::fs::message::inotify *inotify) {   ....   if (inotify->flags           &amp; (purefs::fs::inotify_flags::close_write            | purefs::fs::inotify_flags::move_dst))    {     ....   }   else if (inotify->flags                &amp; ( purefs::fs::inotify_flags::del                  | purefs::fs::inotify_flags::move_src))    {     ....   }   .... } <\/code><\/pre>\n<h3>Conclusion<\/h3>\n<p>In this article, I&#8217;ve described only a part of all GA warnings that PVS-Studio found in this project. In fact, there are more of them. It&#8217;s also worth pointing out that it&#8217;s not the end \u2014 I&#8217;ll write more on the interesting things that the PVS-Studio analyzer found in MuditaOS. We will have at least one more article where we will keep looking to answer one simple question \u2014 &#171;Will your alarm clock ring after all?&#187;<\/p>\n<p>We also recommend MuditaOS developers to run the<a href=\"https:\/\/pvs-studio.com\/en\/pvs-studio\/download\/\"> PVS-Studio<\/a> analyzer on their own for their project and inspect the problem areas. This is <a href=\"https:\/\/pvs-studio.com\/pvs-studio\/try-free\/?utm_source=habr&amp;utm_medium=articles&amp;utm_content=muditaos_p1&amp;utm_term=link_try-free\">free <\/a>for open-source projects.<\/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\/649089\/\"> https:\/\/habr.com\/ru\/articles\/649089\/<\/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-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>Operating systems are a kind of software where code quality is critical. This time the PVS-Studio analyzer checked MuditaOS. So let&#8217;s take a look at what the static analyzer found in this open-source OS.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h3>About the project<\/h3>\n<p><a href=\"https:\/\/mudita.com\/\">MuditaOS<\/a> is an operating system based on FreeRTOS that PVS-Studio checked a while ago. What did we find? Check out <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/cpp\/0684\/\">this article<\/a>! MuditaOS runs on Mudita devices that include a phone, alarm clocks, and a watch. The source code is in C and C++. So. Why don&#8217;t we take a look? How good are these alarm clocks, really? \ud83d\ude42<\/p>\n<p>We followed the <a href=\"https:\/\/github.com\/mudita\/MuditaOS\/blob\/master\/doc\/quickstart.md\">instructions<\/a> from the official repository and built the project under Ubuntu 20.04. We checked the debug version for the Mudita Bell alarm clock. At the end of 2021 the alarm clock cost <em>$60<\/em>. This is what it looked like:<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>Since the project is regularly updated, I froze it in version <a href=\"https:\/\/github.com\/mudita\/MuditaOS\/tree\/8cc1f779db7824a665240c5a89f1590a5831502c\">8cc1f77<\/a>.<\/p>\n<h3>The analyzer&#8217;s warnings<\/h3>\n<h4>Warnings N1\u2013N3<\/h4>\n<p>Before moving on to errors, I&#8217;ll tell you about one amusing case. I&#8217;ve recently given a lecture at Tula State University about undefined behavior. Here&#8217;s what I wrote on the <em>bio<\/em> slide:<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>This requires a bit of a clarification. During code analysis, the PVS-Studio analyzer builds an abstract syntax tree that represents the project&#8217;s code. This is one of the intermediate stages of analysis. The tree&#8217;s nodes represent various language constructs. The latter ones are positioned according to the inheritance hierarchy. From node to node, the language constructs are converted through casts.<\/p>\n<p>When I was just starting out at PVS-Studio, I crashed the analyzer several times (during trial runs), because I was too sure that I knew the type of the node to which I was casting the base type node.<\/p>\n<p>Today I&#8217;ll prove to you that, same as me, MuditaOS developers do not like checking type casts&#8217; results too much. Let&#8217;s see what the analyzer warns about:<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v595\/\">V595<\/a> [CERT-EXP12-C] The &#8216;result&#8217; pointer was utilized before it was verified against nullptr. Check lines: 81, 82. AudioModel.cpp 81<\/p>\n<pre><code class=\"cpp\">void AudioModel::play(....) {   ....   auto cb = [_callback = callback, this](auto response)              {               auto result = dynamic_cast                             &lt;service::AudioStartPlaybackResponse *>(response);               lastPlayedToken = result->token;               if (result == nullptr)                {                 ....               }               ....             };   .... } <\/code><\/pre>\n<p>In this code fragment, the developer uses <em>dynamic_cast<\/em> for type casting. This operation&#8217;s result is a potentially null pointer that is later dereferenced. Then, this the pointer is checked for <em>nullptr<\/em>.<\/p>\n<p>Fixing this code is easy. First, check the <em>result<\/em> pointer for null. Then use it.<\/p>\n<p>Below are two cases that are even more interesting:<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v757\/\">V757<\/a> [CERT-EXP12-C] It is possible that an incorrect variable is compared with nullptr after type conversion using &#8216;dynamic_cast&#8217;. Check lines: 214, 214. CallLogDetailsWindow.cpp 214<\/p>\n<pre><code class=\"cpp\">void CallLogDetailsWindow::onBeforeShow(...., SwitchData *data) {   ....   if (auto switchData = dynamic_cast                         &lt;calllog::CallLogSwitchData *>(data); data != nullptr)    {     ....   }   .... } <\/code><\/pre>\n<p>Here developer uses <em>dynamic_cast<\/em> to cast the pointer to the base class, to the pointer to the derivative. Then the pointer being cast is checked for <em>nullptr<\/em>. However, most likely, the developer intended to check the cast&#8217;s result for <em>nullptr<\/em>. In case this is indeed a typo, one can fix the code as follows:<\/p>\n<pre><code class=\"cpp\">void CallLogDetailsWindow::onBeforeShow(...., SwitchData *data) {   ....   if (auto switchData = dynamic_cast&lt;calllog::CallLogSwitchData *>(data))    {     ....   }   .... } <\/code><\/pre>\n<p>It&#8217;s possible not everyone likes this fix, but we consider it short and convenient \u2014 we initialize and check the pointer in one operation \u2014 which is why we use the approach everywhere.<\/p>\n<p>Note. This is different from the case when an existing variable is assigned inside a condition. The code below is considered poor practice:<\/p>\n<pre><code class=\"cpp\">int x = ...; if (x = foo()) <\/code><\/pre>\n<p>It&#8217;s not clear whether they attempted to write a comparison, but made a typo or whether they truly intended to assign and check the variable simultaneously. Most compilers and analyzers <a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v559\/\">warn<\/a> about such code \u2014and rightfully so. The code is dangerous and unclear. However, it&#8217;s a completely different matter when someone creates a new variable as is shown in the example. There someone attempted to create a new variable and initialize it with a specific value. You wouldn&#8217;t be able to perform the == operation there, no matter how bad you might want it.<\/p>\n<p>Let&#8217;s get back to the project&#8217;s code. Below is one similar case:<\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v757\/\">V757<\/a> [CERT-EXP12-C] It is possible that an incorrect variable is compared with nullptr after type conversion using &#8216;dynamic_cast&#8217;. Check lines: 47, 47. PhoneNameWindow.cpp 47<\/p>\n<pre><code class=\"cpp\">void PhoneNameWindow::onBeforeShow(ShowMode \/*mode*\/, SwitchData *data) {   if (const auto newData = dynamic_cast&lt;PhoneNameData *>(data);                                                              data != nullptr)    {     ....   } } <\/code><\/pre>\n<p>The correct code looks like this:<\/p>\n<pre><code class=\"cpp\">void PhoneNameWindow::onBeforeShow(ShowMode \/*mode*\/, SwitchData *data) {   if (const auto newData = dynamic_cast&lt;PhoneNameData *>(data))    {     ....   } } <\/code><\/pre>\n<p>Note that simplifying such checks is one of our code refactoring recommendations we covered in <a href=\"https:\/\/pvs-studio.com\/en\/blog\/video\/10513\/\">this video.<\/a> Do take a look if you haven&#8217;t already! It&#8217;s short and you may learn something new \ud83d\ude42<\/p>\n<h4>Warning N4<\/h4>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v522\/\">V522<\/a> [CERT-EXP34-C] Dereferencing of the null pointer &#8216;document&#8217; might take place. TextBlockCursor.cpp 332<\/p>\n<pre><code class=\"cpp\">auto BlockCursor::begin() -> std::list&lt;TextBlock>::iterator {   return document == nullptr              ? document->blocks.end() : document->blocks.begin(); } <\/code><\/pre>\n<p>This code fragment deserves its very own facepalm. Let&#8217;s figure out what happens here. The developer explicitly checks the <em>document<\/em> pointer for <em>nullptr<\/em>. Then the pointer is dereferenced in both branches of the ternary operator. The code is correct only if the developer aimed to crash the program.<\/p>\n<h4>Warning N5<\/h4>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v517\/\">V517<\/a> [CERT-MSC01-C] The use of &#8216;if (A) {&#8230;} else if (A) {&#8230;}&#8217; pattern was detected. There is a probability of logical error presence. Check lines: 1053, 1056. avdtp_util.c 1053<\/p>\n<pre><code class=\"cpp\">static uint16_t avdtp_signaling_setup_media_codec_mpeg_audio_config_event(....) {   uint8_t channel_mode_bitmap = ....;   ....   if (....)   {     ....   }   else if (channel_mode_bitmap &amp; 0x02)   {     num_channels = 2;     channel_mode = AVDTP_CHANNEL_MODE_STEREO;   }   else if (channel_mode_bitmap &amp; 0x02)   {     num_channels = 2;     channel_mode = AVDTP_CHANNEL_MODE_JOINT_STEREO;   }   .... } <\/code><\/pre>\n<p>Here we can see classic copy-pasted code. There are two ways to understand and fix this code: either the second branch should contain a different check, or the second check is redundant and needs to be removed. Since the two branches contain different logic, I assume the first variant applies here. In any case, I recommend MuditaOS developers to take a look at this code snippet.<\/p>\n<h4>Warnings N6, N7<\/h4>\n<ul>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v571\/\">V571<\/a> Recurring check. The &#8216;if (activeInput)&#8217; condition was already verified in line 249. ServiceAudio.cpp 250<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v547\/\">V547<\/a> Expression &#8216;activeInput&#8217; is always true. ServiceAudio.cpp 250<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"cpp\">std::optional&lt;AudioMux::Input *> AudioMux::GetActiveInput();  ....  auto Audio::handleSetVolume(....) -> std::unique_ptr&lt;AudioResponseMessage> {   ....   if (const auto activeInput = audioMux.GetActiveInput(); activeInput)    {     if (activeInput)      {       retCode = activeInput.value()->audio->SetOutputVolume(clampedValue);     }   }   .... } <\/code><\/pre>\n<p>Let&#8217;s investigate. The <em>activeinput<\/em> type is an <em>std::optional<\/em> entity from the pointer to <em>AudioMax::input<\/em>. The nested <em>if<\/em> statement contains the <em>value member function call.<\/em> The function is guaranteed to return the pointer and will not throw an exception. After, the result is dereferenced.<\/p>\n<p>However, the function may return either a valid \u2014 or a null pointer. The plan for the nested <em>if<\/em> statement was probably to check this pointer. Hm, I also like wrapping pointers and boolean values in <em>std::optional<\/em>! And then going through the same grief each time :).<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>The fixed code:<\/p>\n<pre><code class=\"cpp\">std::optional&lt;AudioMux::Input *> AudioMux::GetActiveInput();  ....  auto Audio::handleSetVolume(....) -> std::unique_ptr&lt;AudioResponseMessage> {   ....   if (const auto activeInput = audioMux.GetActiveInput(); activeInput)    {     if (*activeInput)      {       retCode = (*activeInput)->audio->SetOutputVolume(clampedValue);     }   }   .... } <\/code><\/pre>\n<h4>Warning N8\u2013N11<\/h4>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v668\/\">V668<\/a> [CERT-MEM52-CPP] There is no sense in testing the &#8216;pcBuffer&#8217; pointer against null, as the memory was allocated using the &#8216;new&#8217; operator. The exception will be generated in the case of memory allocation error. syscalls_stdio.cpp 384<\/p>\n<pre><code class=\"cpp\">int _iosys_fprintf(FILE *__restrict __stream,                    const char *__restrict __format, ...) {   constexpr auto buf_len = 4096;   char *pcBuffer;   ....   pcBuffer = new char[buf_len];   if (pcBuffer == NULL)    {     ....   } } <\/code><\/pre>\n<p>Here the pointer value, that the <em>new<\/em> operator (which is not overloaded, as far as I can tell) returns*,* is compared to <em>NULL<\/em>. However, if the <em>new<\/em> operator fails to allocate memory, then, according to the language standard, the <em>std::bad_alloc()<\/em> exception is generated. Consequently, checking the pointer for null makes no sense.<\/p>\n<p>Even less so in the code of an operating system that functions in real time. Most likely, in cases when memory cannot be allocated, the program will crash and the code that follows will be simply unreachable.<\/p>\n<p>The check may take place if the <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/memory\/new\/nothrow\"><em>nothrow<\/em><\/a> overload of <em>new<\/em> is employed:<\/p>\n<pre><code class=\"cpp\">int _iosys_fprintf(FILE *__restrict __stream,                    const char *__restrict __format, ...) {   constexpr auto buf_len = 4096;   char *pcBuffer;   ....   pcBuffer = new (std::nothrow) char[buf_len];   if (pcBuffer == NULL)    {     ....   } } <\/code><\/pre>\n<p>The analyzer found several more such cases.<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v668\/\">V668<\/a> [CERT-MEM52-CPP] There is no sense in testing the &#8216;fontData&#8217; pointer against null, as the memory was allocated using the &#8216;new&#8217; operator. The exception will be generated in the case of memory allocation error. FontManager.cpp 56<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/docs\/warnings\/v668\/\">V668<\/a> [CERT-MEM52-CPP] There is no sense in <\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\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-380706","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/380706","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=380706"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/380706\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=380706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=380706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=380706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}