{"id":397251,"date":"2024-06-29T13:20:04","date_gmt":"2024-06-29T13:20:04","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=397251"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=397251","title":{"rendered":"<span>PVS-Studio searches for bugs in the DuckStation project<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>We often check retro games. In our company, many developers like to find interesting projects for themselves. They feel nostalgic when they&#8217;re studying these projects. But we need to run retro games on something, right? This time we checked a project that helps to run old games on modern hardware.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/7ff\/1b0\/555\/7ff1b055557aee2c93500ac374a8c1f4.png\" alt=\"0881_duckstation\/image1.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/7ff\/1b0\/555\/7ff1b055557aee2c93500ac374a8c1f4.png\"\/><\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h2 id=\"introduction\">Introduction<\/h2>\n<p>  <\/p>\n<p>DuckStation is an emulator of the Sony PlayStation console. The emulator, according to its website, has a version for Windows, Linux, and for Android smartphones. And recently it was launched on Xbox Series X and S. The project itself contains slightly less than a million lines of C and C++ code. DuckStation doesn&#8217;t release updates. Its developers regularly commit changes. So, we had to fixate the SHA of the commit: <em><a href=\"https:\/\/github.com\/stenzek\/duckstation\/tree\/13c5ee8bfb4f0f8fc40f76b39de58b5d9b473dc3\">13c5ee8<\/a><\/em>.<\/p>\n<p>  <\/p>\n<p>We checked the project and found a lot of warnings \u2014 170 of the High level and 434 of the Medium level. Let&#8217;s look at the 10 most exciting of them.<\/p>\n<p>  <\/p>\n<h2 id=\"check-results\">Check results<\/h2>\n<p>  <\/p>\n<p><strong>Warning N1<\/strong><\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v726\/\">V726<\/a> An attempt to free memory containing the &#8216;wbuf&#8217; array by using the &#8216;free&#8217; function. This is incorrect as &#8216;wbuf&#8217; was created on stack. log.cpp 216<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">template&lt;typename T> static ALWAYS_INLINE void FormatLogMessageAndPrintW(....) {   ....   wchar_t wbuf[512];   wchar_t* wmessage_buf = wbuf;   ....   if (wmessage_buf != wbuf)   {     std::free(wbuf);   }   if (message_buf != buf)   {     std::free(message_buf);   }   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Here the analyzer detected code with an error. In this code fragment, we see an attempt to delete an array allocated on the stack. Since the memory has not been allocated on the heap, you don&#8217;t need to call any special functions like std::free to clear it. When the object is destroyed, the memory is cleared automatically.<\/p>\n<p>  <\/p>\n<p>Also, when my colleague was editing this article, he considered this warning a false-positive. I described this interesting case in a separate article. So, I invite you to read it: <em><a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/cpp\/0880\/\">How a PVS-Studio developer defended a bug in a checked project<\/a><\/em>.<\/p>\n<p>  <\/p>\n<p><strong>Warning N2<\/strong><\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v547\/\">V547<\/a> Expression &#8216;i &lt; pathLength&#8217; is always true. file_system.cpp 454<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">void CanonicalizePath(const char *Path, ....) {   ....   u32 pathLength = static_cast&lt;u32>(std::strlen(Path));   ....   for (i = 0; i &lt; pathLength;)   {     ....     char nextCh = (i &lt; pathLength) ? Path[i + 1] : '\\0';     ....   }   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>The induction variable <em>i<\/em> increases after the initialization of <em>nextCh<\/em>. Judging by the fact that the <em>strlen<\/em> function is used to determine the string length, the <em>Path<\/em> string is <a href=\"https:\/\/pvs-studio.com\/en\/blog\/terms\/0088\/\">null-terminated<\/a>. Then the <em>i &lt; pathLength<\/em> check is clearly redundant. You can skip the check since the condition will always be true. During the last loop iteration, we will get the null character anyway. Then the following code:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">char nextCh = (i &lt; pathLength) ? Path[i + 1] : '\\0';<\/code><\/pre>\n<p>  <\/p>\n<p>is the equivalent of:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">char nextCh = Path[i + 1];<\/code><\/pre>\n<p>  <\/p>\n<p>However, even if the string wasn&#8217;t null-terminated, the check would be incorrect. During the final loop iteration, when trying to take the last character from <em>Path[i + 1]<\/em>, you will get outside the array boundaries. In this case, the following code fragment would be better:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">char nextCh = ((i + 1) &lt; pathLength) ? Path[i + 1] : '\\0';<\/code><\/pre>\n<p>  <\/p>\n<p><strong>Warnings N3, N4<\/strong><\/p>\n<p>  <\/p>\n<p>For this code snippet, the analyzer issued two warnings at once:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/pvs-studio.com\/en\/w\/v547\/\">V547<\/a> Expression &#8216;m_value.wSecond &lt;= other.m_value.wSecond&#8217; is always true. timestamp.cpp 311<\/li>\n<li><a href=\"https:\/\/pvs-studio.com\/en\/w\/v779\/\">V779<\/a> Unreachable code detected. It is possible that an error is present. timestamp.cpp 314<\/li>\n<\/ul>\n<p>  <\/p>\n<pre><code class=\"cpp\">bool Timestamp::operator&lt;=(const Timestamp&amp; other) const {   ....   if (m_value.wYear > other.m_value.wYear)     return false;   else if (m_value.wYear &lt; other.m_value.wYear)     return true;   if (m_value.wMonth > other.m_value.wMonth)     return false;   else if (m_value.wMonth &lt; other.m_value.wMonth)     return true;   if (m_value.wDay > other.m_value.wDay)     return false;   else if (m_value.wDay &lt; other.m_value.wDay)     return true;   if (m_value.wHour > other.m_value.wHour)     return false;   else if (m_value.wHour &lt; other.m_value.wHour)     return true;   if (m_value.wMinute > other.m_value.wMinute)     return false;   else if (m_value.wMinute &lt; other.m_value.wMinute)     return true;   if (m_value.wSecond > other.m_value.wSecond)     return false;   else if (m_value.wSecond &lt;= other.m_value.wSecond) \/\/ &lt;=     return true;   if (m_value.wMilliseconds > other.m_value.wMilliseconds)     return false;   else if (m_value.wMilliseconds &lt; other.m_value.wMilliseconds)     return true;    return false; }<\/code><\/pre>\n<p>  <\/p>\n<p>Here the operator compares values from a year to milliseconds. However, the error apparently occurred already in the code line that compared seconds. The <em>&lt;=<\/em> sign forgotten (or misprinted) when seconds are checked made subsequent operations unreachable.<\/p>\n<p>  <\/p>\n<p>The error was repeated. The second time it was a similar <em>operator >=<\/em>. The analyzer issued two warnings as well:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/pvs-studio.com\/en\/w\/v547\/\">V547<\/a> Expression &#8216;m_value.wSecond >= other.m_value.wSecond&#8217; is always true. timestamp.cpp 427<\/li>\n<li><a href=\"https:\/\/pvs-studio.com\/en\/w\/v779\/\">V779<\/a> Unreachable code detected. It is possible that an error is present. timestamp.cpp 430<\/li>\n<\/ul>\n<p>  <\/p>\n<p>By the way, my colleague wrote an excellent <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/cpp\/0509\/\">article<\/a> on the topic of comparison functions. In his article, he shows various examples of patterns similar to the errors described above.<\/p>\n<p>  <\/p>\n<p><strong>Warning N5<\/strong><\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v583\/\">V583<\/a> The &#8216;?:&#8217; operator, regardless of its conditional expression, always returns one and the same value. gamelistmodel.cpp 415<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">bool GameListModel::lessThan(...., int column, bool ascending) const {   ....   const GameListEntry&amp; left  = m_game_list->GetEntries()[left_row];   const GameListEntry&amp; right = m_game_list->GetEntries()[right_row];   ....   switch(column)   {     case Column_Type:     {       ....       return ascending ?               (static_cast&lt;int>(left.type)             &lt;  static_cast&lt;int>(right.type))             :              (static_cast&lt;int>(right.type)             >  static_cast&lt;int>(left.type));     }   }   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>We have two identical comparisons here. The conditional operator&#8217;s operands, located on both sides of the greater than and less than signs, are simply swapped in two branches of the operator. In fact, the code fragment in the <em>return<\/em> operator is equivalent to:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">return ascending ?               (static_cast&lt;int>(left.type)             &lt;  static_cast&lt;int>(right.type))             :              (static_cast&lt;int>(left.type)             &lt;  static_cast&lt;int>(right.type));<\/code><\/pre>\n<p>  <\/p>\n<p>Probably, the code should look as follows:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">return ascending ?               (static_cast&lt;int>(left.type)             &lt;  static_cast&lt;int>(right.type))            :              (static_cast&lt;int>(right.type)             &lt;  static_cast&lt;int>(left.type));<\/code><\/pre>\n<p>  <\/p>\n<p><strong>Warnings N6, N7, N8<\/strong><\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v501\/\">V501<\/a> There are identical sub-expressions &#8216;c != &#8216; &#187; to the left and to the right of the &#8216;&amp;&amp;&#8217; operator. file_system.cpp 560<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">static inline bool FileSystemCharacterIsSane(char c, ....) {   if    (!(c >= 'a' &amp;&amp; c &lt;= 'z')       &amp;&amp; !(c >= 'A' &amp;&amp; c &lt;= 'Z')       &amp;&amp; !(c >= '0' &amp;&amp; c &lt;= '9')       &amp;&amp;   c != ' '       &amp;&amp;   c != ' '       &amp;&amp;   c != '_'       &amp;&amp;   c != '-'       &amp;&amp;   c != '.')   {     ....   }   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>In this case, an extra check for space occurs twice. Also, the analyzer issued a few more similar warnings:<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v501\/\">V501<\/a> There are identical sub-expressions to the left and to the right of the &#8216;|&#8217; operator: KMOD_LCTRL | KMOD_LCTRL sdl_key_names.h 271<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">typedef enum {   KMOD_NONE   = 0x0000,   KMOD_LSHIFT = 0x0001,   KMOD_RSHIFT = 0x0002,   KMOD_LCTRL  = 0x0040,   .... } .... static const std::array&lt;SDLKeyModifierEntry, 4> s_sdl_key_modifiers =  {   {{KMOD_LSHIFT, static_cast&lt;SDL_Keymod>(KMOD_LSHIFT | KMOD_RSHIFT),     SDLK_LSHIFT, SDLK_RSHIFT, \"Shift\"},   {KMOD_LCTRL, static_cast&lt;SDL_Keymod>(KMOD_LCTRL | KMOD_LCTRL), \/\/ &lt;=     SDLK_LCTRL, SDLK_RCTRL, \"Control\"},   {KMOD_LALT, static_cast&lt;SDL_Keymod>(KMOD_LALT | KMOD_RALT),     SDLK_LALT, SDLK_RALT, \"Alt\"},   {KMOD_LGUI, static_cast&lt;SDL_Keymod>(KMOD_LGUI | KMOD_RGUI),     SDLK_LGUI, SDLK_RGUI, \"Meta\"}} };<\/code><\/pre>\n<p>  <\/p>\n<p>Here we have identical <em>KMOD_LCTRL<\/em> sub-expressions to the left and to the right of the <em>|<\/em> operator. It looks suspicious.<\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v501\/\">V501<\/a> There are identical sub-expressions &#8216;TokenMatch(command, &#171;CATALOG&#187;)&#8217; to the left and to the right of the &#8216;||&#8217; operator. cue_parser.cpp 196<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">bool File::ParseLine(const char* line, ....) {   const std::string_view command(GetToken(line));   ....   if (   TokenMatch(command, \"CATALOG\") \/\/ &lt;=       || TokenMatch(command, \"CDTEXTFILE\")        || TokenMatch(command, \"CATALOG\") \/\/ &lt;=       || TokenMatch(command, \"ISRC\")        || TokenMatch(\"command\", \"TRACK_ISRC\")        || TokenMatch(command, \"TITLE\")       ||  ....)   {     ....   }   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Here, the <em>TokenMatch<\/em> function is called twice.<\/p>\n<p>  <\/p>\n<p>Intriguingly, in the check below, there is also an error: <em>command<\/em> is written as a string literal instead of a variable. By the way, we&#8217;ve been meaning to make a diagnostic rule that will allow to monitor such situations. This code fragment is one of the indicators that such diagnostic will be useful.<\/p>\n<p>  <\/p>\n<p>Maybe, in all these cases, in place of redundant checks there should have been checks for other values. That&#8217;s why the code fragments do not work as expected by the developers who wrote them.<\/p>\n<p>  <\/p>\n<p><strong>Warning N9<\/strong><\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v1065\/\">V1065<\/a> Expression can be simplified, check &#8216;m_display_texture_height&#8217; and similar operands. host_display.cpp 549<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">.... s32 m_display_texture_height = ....; s32 m_display_texture_view_y = ....; .... bool HostDisplay::WriteDisplayTextureToFile(....) {   s32 read_y = m_display_texture_view_y;   s32 read_height = m_display_texture_view_height;    ....   read_y = (m_display_texture_height - read_height) \u2013            (m_display_texture_height - m_display_texture_view_y);   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Yes, this code fragment doesn&#8217;t contain an error. But we can slightly shorten the code by simplifying the expression:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">read_y = m_display_texture_view_y - read_height;<\/code><\/pre>\n<p>  <\/p>\n<p>To tell the truth, this is not a serious warning and I should not add it to the article. However, I added, simply because this is the warning of my diagnostic. I am pleased that it worked \ud83d\ude42<\/p>\n<p>  <\/p>\n<p><strong>Warning N10<\/strong><\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v614\/\">V614<\/a> The &#8216;host_interface&#8217; smart pointer is utilized immediately after being declared or reset. It is suspicious that no value was assigned to it. main.cpp 45<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">static std::unique_ptr&lt;NoGUIHostInterface> CreateHostInterface() {   const char* platform = std::getenv(\"DUCKSTATION_NOGUI_PLATFORM\");   std::unique_ptr&lt;NoGUIHostInterface> host_interface;  #ifdef WITH_SDL2   if (   !host_interface &amp;&amp; (!platform        || StringUtil::Strcasecmp(platform, \"sdl\") == 0)        &amp;&amp; IsSDLHostInterfaceAvailable())   {     host_interface = SDLHostInterface::Create();   }   } #endif  #ifdef WITH_VTY   if (  !host_interface &amp;&amp; (!platform        || StringUtil::Strcasecmp(platform, \"vty\") == 0))   {     host_interface = VTYHostInterface::Create();   } #endif  #ifdef _WIN32   if (  !host_interface &amp;&amp; (!platform        || StringUtil::Strcasecmp(platform, \"win32\") == 0))   {     host_interface = Win32HostInterface::Create();   }  #endif    return host_interface; }<\/code><\/pre>\n<p>  <\/p>\n<p>According to the diagnostic, the code contains an uninitialized variable. There is a meaningless smart pointer check going on here. First check: <em>!host_interface<\/em> will always return <em>true<\/em>. <\/p>\n<p>  <\/p>\n<p>It would seem that the error is not very critical, and the redundant code is written to maintain the overall coding style. It&#8217;s possible to rewrite the code so it is even more readable:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">static std::unique_ptr&lt;NoGUIHostInterface> CreateHostInterface() {   const char* platform = std::getenv(\"DUCKSTATION_NOGUI_PLATFORM\"); #ifdef WITH_SDL2   if (   (!platform        || StringUtil::Strcasecmp(platform, \"sdl\") == 0)        &amp;&amp; IsSDLHostInterfaceAvailable())   {     return SDLHostInterface::Create();   } #endif  #ifdef WITH_VTY   if (   !platform        || StringUtil::Strcasecmp(platform, \"vty\") == 0)   {     return VTYHostInterface::Create();   } #endif  #ifdef _WIN32   if (   !platform        || StringUtil::Strcasecmp(platform, \"win32\") == 0)   {     return Win32HostInterface::Create();   } #endif    return {}; }<\/code><\/pre>\n<p>  <\/p>\n<p>Seems that now we have four <em>return <\/em>statements instead of one. Code is supposed to work slower, however, I wrote a similar synthetic code <a href=\"https:\/\/godbolt.org\/z\/PWPT6P4ve\">example<\/a>. As you can see, under the <em>O2<\/em> optimizations, the <em>Slang 13<\/em> and <em>GCC<\/em> <em>11.2 <\/em>compilers generate fewer assembly instructions for the second example (it is especially evident for <em>GCC<\/em>).<\/p>\n<p>  <\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>  <\/p>\n<p>Even though the project is not that big, the analyzer issued some fascinating warnings. I hope this article will help the DuckStation developers fix some bugs. Maybe they will want to double-check their code base using PVS-Studio.<\/p>\n<p>  <\/p>\n<p>If you want to try PVS-Studio on your project, you can download it <a href=\"https:\/\/pvs-studio.com\/en\/pvs-studio\/download\/\">here<\/a>.<\/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\/587238\/\"> https:\/\/habr.com\/ru\/articles\/587238\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>We often check retro games. In our company, many developers like to find interesting projects for themselves. They feel nostalgic when they&#8217;re studying these projects. But we need to run retro games on something, right? This time we checked a project that helps to run old games on modern hardware.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/7ff\/1b0\/555\/7ff1b055557aee2c93500ac374a8c1f4.png\" alt=\"0881_duckstation\/image1.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/7ff\/1b0\/555\/7ff1b055557aee2c93500ac374a8c1f4.png\"\/><\/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-397251","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/397251","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=397251"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/397251\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=397251"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=397251"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=397251"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}