{"id":408794,"date":"2024-06-29T20:23:09","date_gmt":"2024-06-29T20:23:09","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=408794"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=408794","title":{"rendered":"<span>PVS-Studio to check the RPCS3 emulator<\/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>RPCS3 is an interesting project that emulates the PS3 console. It is actively evolving. Recently we heard the news that the emulator learned how run all the games from the console&#8217;s catalog. That&#8217;s a good excuse to analyze the project. We&#8217;ll see which errors remained after new fixes were added to the project.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/c7d\/156\/f1c\/c7d156f1c49a1ec607de9aca78a1e872.png\" alt=\"0886_rpcs3\/image1.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/c7d\/156\/f1c\/c7d156f1c49a1ec607de9aca78a1e872.png\"\/><\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<h2 id=\"introduction\">Introduction<\/h2>\n<p>  <\/p>\n<p>The project is quite hefty. It contains about 300 thousand lines of C++ code and relies upon many external dependencies that include the following:<\/p>\n<p>  <\/p>\n<ul>\n<li>llvm, a toolkit for writing compilers and utilities. By the way, we&#8217;ve recently checked <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/cpp\/0871\/\">LLVM 13<\/a>;<\/li>\n<li>ffmpeg, a library for working with media files;<\/li>\n<li>curl, helpful in network interactions and for work with the HTTP protocol;<\/li>\n<li>zlib, a data compression library that uses the DEFLATE algorithm.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>For the GUI part, the project uses Qt \u2014 however, that is taken from the system library. The screenshot below demonstrates the full list of dependencies:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/ccc\/f6b\/f13\/cccf6bf13eb0ef2750410803f7f999ad.png\" alt=\"0886_rpcs3\/image2.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/ccc\/f6b\/f13\/cccf6bf13eb0ef2750410803f7f999ad.png\"\/><\/p>\n<p>  <\/p>\n<p>Note, that the C++ standard used, is the latest one, C++20. PVS-Studio handles checking such modern code very well. This is because we are constantly working to support innovations. Yes, there are some things to improve yet \u2014 and we are working on fixing them. Overall, the check was a good test of how the analyzer supports new language constructs.<\/p>\n<p>  <\/p>\n<p>The RPCS3 project uses the CMake build system. Unfortunately, I experienced some problems during the build \u2014 GCC 11.2 refused to compile some constexpr construction. Clang, however, handled the build perfectly. I built the project on Ubuntu&#8217;s developer version, so the problem I experienced could be related to the distribution.<\/p>\n<p>  <\/p>\n<p>The entire procedure of building and checking the project on Linux in the <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/cpp\/0851\/\">intermodular analysis<\/a> mode looks as follows:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">cmake -S. -Bbuild -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCMAKE_BUILD_TYPE=Debug \\           -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ cmake --build build -j$(nproc) pvs-studio-analyzer analyze -f .\/build\/compile_commands.json -j`nproc` \\           -o pvs.log -e 3rdparty\/ -e llvm\/ --intermodular<\/code><\/pre>\n<p>  <\/p>\n<p>Alright, the analysis is all done! Time to look at errors!<\/p>\n<p>  <\/p>\n<h2 id=\"dont-code-in-std-bro\">Don&#8217;t code in std, bro<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v1061\/\">V1061<\/a> Extending the &#8216;std&#8217; namespace may result in undefined behavior. shared_ptr.hpp 1131<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">namespace std {   template &lt;typename T>   void swap(stx::single_ptr&lt;T>&amp; lhs, stx::single_ptr&lt;T>&amp; rhs) noexcept   {     lhs.swap(rhs);   }    template &lt;typename T>   void swap(stx::shared_ptr&lt;T>&amp; lhs, stx::shared_ptr&lt;T>&amp; rhs) noexcept   {     lhs.swap(rhs);   } }<\/code><\/pre>\n<p>  <\/p>\n<p>The C++ standard explicitly prohibits defining user function templates in the <em>std<\/em> namespace. C++20 also prohibits defining specializations for function templates. Defining the <em>swap<\/em> custom function is a frequent error of this kind. In this case, you can do the following:<\/p>\n<p>  <\/p>\n<ul>\n<li>define the <em>swap<\/em> function in the same namespace where the class is defined (<em>stx<\/em>);<\/li>\n<li>add the <em>using std::swap<\/em> directive to the block that requires calling the <em>swap<\/em> function;<\/li>\n<li>call swap without specifying the <em>std<\/em> namespace, i.e. do an unqualified function call: <em>swap(obj1, obj2)<\/em>;<\/li>\n<\/ul>\n<p>  <\/p>\n<p>This approach uses the Argument-Dependent Lookup (ADL) mechanism. As a result, the compiler finds the <em>swap<\/em> function that we defined next to the class. The <em>std<\/em> namespace remains unchanged.<\/p>\n<p>  <\/p>\n<h2 id=\"deleted-memset\">Deleted memset<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v597\/\">V597<\/a> The compiler could delete the &#8216;memset&#8217; function call, which is used to flush &#8216;cty&#8217; object. The memset_s() function should be used to erase the private data. aes.cpp 596<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">\/*  * AES key schedule (decryption)  *\/ int aes_setkey_dec(....) {     aes_context cty;      \/\/ ....  done:     memset( &amp;cty, 0, sizeof( aes_context ) );      return( 0 ); }<\/code><\/pre>\n<p>  <\/p>\n<p>This is a frequent error. When optimizing the code, the compiler removes the <em>memset<\/em> call, while private data remains in the memory. Yes, in case of the emulator this hardly poses any data leakage threat \u2014 but either way, the error is present.<\/p>\n<p>  <\/p>\n<p>PVS-Studio found more locations with this type of an error:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/pvs-studio.com\/en\/w\/v597\/\">V597<\/a> The compiler could delete the &#8216;memset&#8217; function call, which is used to flush &#8216;tmpbuf&#8217; buffer. The memset_s() function should be used to erase the private data. sha1.cpp 371<\/li>\n<li><a href=\"https:\/\/pvs-studio.com\/en\/w\/v597\/\">V597<\/a> The compiler could delete the &#8216;memset&#8217; function call, which is used to flush &#8216;ctx&#8217; object. The memset_s() function should be used to erase the private data. sha1.cpp 396<\/li>\n<\/ul>\n<p>  <\/p>\n<h2 id=\"redundant-check\">Redundant check<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v547\/\">V547<\/a> Expression &#8216;rawcode == CELL_KEYC_KPAD_NUMLOCK&#8217; is always false. cellKb.cpp 126<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">enum Keys {   \/\/ ....   CELL_KEYC_KPAD_NUMLOCK          = 0x53,   \/\/ .... };  u16 cellKbCnvRawCode(u32 arrange, u32 mkey, u32 led, u16 rawcode) {   \/\/ ....    \/\/ CELL_KB_RAWDAT   if (rawcode &lt;= 0x03       || rawcode == 0x29       || rawcode == 0x35       || (rawcode >= 0x39 &amp;&amp; rawcode &lt;= 0x53)    \/\/ &lt;=       || rawcode == 0x65       || rawcode == 0x88       || rawcode == 0x8A       || rawcode == 0x8B)   {     return rawcode | 0x8000;   }    const bool is_alt = mkey &amp; (CELL_KB_MKEY_L_ALT | CELL_KB_MKEY_R_ALT);   const bool is_shift = mkey &amp; (CELL_KB_MKEY_L_SHIFT | CELL_KB_MKEY_R_SHIFT);   const bool is_caps_lock = led &amp; (CELL_KB_LED_CAPS_LOCK);   const bool is_num_lock = led &amp; (CELL_KB_LED_NUM_LOCK);    \/\/ CELL_KB_NUMPAD    if (is_num_lock)   {     if (rawcode == CELL_KEYC_KPAD_NUMLOCK)  return 0x00 | 0x4000; \/\/ &lt;=     if (rawcode == CELL_KEYC_KPAD_SLASH)    return 0x2F | 0x4000;     if (rawcode == CELL_KEYC_KPAD_ASTERISK) return 0x2A | 0x4000;     if (rawcode == CELL_KEYC_KPAD_MINUS)    return 0x2D | 0x4000;     if (rawcode == CELL_KEYC_KPAD_PLUS)     return 0x2B | 0x4000;     if (rawcode == CELL_KEYC_KPAD_ENTER)    return 0x0A | 0x4000;     if (rawcode == CELL_KEYC_KPAD_0)        return 0x30 | 0x4000;     if (rawcode >= CELL_KEYC_KPAD_1 &amp;&amp; rawcode &lt;= CELL_KEYC_KPAD_9)       return (rawcode - 0x28) | 0x4000;   } }<\/code><\/pre>\n<p>  <\/p>\n<p>Here the error is hidden in the first condition: this condition blocks the condition below that checks whether the <em>rawcode<\/em> variable value equals the <em>CELL_KEYC_KPAD_NUMLOCK<\/em> constant value. The <em>CELL_KEYC_KPAD_NUMLOCK<\/em> value corresponds to 0x53 \u2014 this number meets the first condition, so the function exits there. Consequently, the lower <em>if<\/em> block is never executed.<\/p>\n<p>  <\/p>\n<p>The error could have been caused by one of the two things \u2014 either the first condition does not take the constant&#8217;s value into account, or the constant is defined incorrectly.<\/p>\n<p>  <\/p>\n<h2 id=\"array-overflow\">Array overflow<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v557\/\">V557<\/a> Array underrun is possible. The value of &#8216;month + \u2014 1&#8217; index could reach -1. cellRtc.cpp 1470<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">error_code cellRtcGetDaysInMonth(s32 year, s32 month) {   cellRtc.todo(\"cellRtcGetDaysInMonth(year=%d, month=%d)\", year, month);    if ((year &lt; 0) || (month &lt; 0) || (month > 12))   {     return CELL_RTC_ERROR_INVALID_ARG;   }    if (is_leap_year(year))   {     return not_an_error(DAYS_IN_MONTH[month + 11]);   }    return not_an_error(DAYS_IN_MONTH[month + -1]); \/\/ &lt;= }<\/code><\/pre>\n<p>  <\/p>\n<p>In the code above, the <em>month<\/em> argument value can be 0. Consequently, the return operator may attempt to access the <em>DAYS_IN_MONTH<\/em> array&#8217;s element that has the -1 index.<\/p>\n<p>  <\/p>\n<p>Most likely, the error is in the first condition. The code above counts months from one, while the condition makes sure that <em>month<\/em> is no less than zero. The correct condition would be <em>month &lt; 1<\/em>.<\/p>\n<p>  <\/p>\n<p>This error reminded me of an interesting case from the protobuf project: <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/cpp\/0550\/\">February 31<\/a>.<\/p>\n<p>  <\/p>\n<h2 id=\"copy-paste-error\">Copy-paste error<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v519\/\">V519<\/a> The &#8216;evnt->color.white_x&#8217; variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 51, 52. sys_uart.cpp 52<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">struct av_get_monitor_info_cmd : public ps3av_cmd {   bool execute(....) override   {     \/\/ ....     evnt->color.blue_x = 0xFFFF;     evnt->color.blue_y = 0xFFFF;     evnt->color.green_x = 0xFFFF;     evnt->color.green_y = 0xFFFF;     evnt->color.red_x = 0xFFFF;     evnt->color.red_y = 0xFFFF;     evnt->color.white_x = 0xFFFF;     evnt->color.white_x = 0xFFFF; \/\/ &lt;=     evnt->color.gamma = 100;     \/\/ ....   { };<\/code><\/pre>\n<p>  <\/p>\n<p>That&#8217;s a common error: when writing a function, a developer copied a line and forgot to change the required variable. And it&#8217;s quite a challenge to spot this error by just reading the code \u2014 while the static analyzer does an excellent job in these cases.<\/p>\n<p>  <\/p>\n<h2 id=\"repeated-checks\">Repeated checks<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v581\/\">V581<\/a> The conditional expressions of the &#8216;if&#8217; statements situated alongside each other are identical. Check lines: 4225, 4226. PPUTranslator.cpp 4226<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">void PPUTranslator::MTFSFI(ppu_opcode_t op) {   SetFPSCRBit(op.crfd * 4 + 0, m_ir->getInt1((op.i &amp; 8) != 0), false);   if (op.crfd != 0) SetFPSCRBit(op.crfd * 4 + 1,                                 m_ir->getInt1((op.i &amp; 4) != 0), false);   if (op.crfd != 0) SetFPSCRBit(op.crfd * 4 + 2,                                 m_ir->getInt1((op.i &amp; 2) != 0), false);   SetFPSCRBit(op.crfd * 4 + 3, m_ir->getInt1((op.i &amp; 1) != 0), false);    if (op.rc) SetCrFieldFPCC(1); }<\/code><\/pre>\n<p>  <\/p>\n<p>This looks like another copy-paste error. Most likely, someone copied the condition and forgot to change it. However, the then part is now different.<\/p>\n<p>  <\/p>\n<p>Interestingly, this is not the only case of such an error. The analyzer found one more error of this kind:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/pvs-studio.com\/en\/w\/v581\/\">V581<\/a> The conditional expressions of the &#8216;if&#8217; statements situated alongside each other are identical. Check lines: 758, 759. RSXThread.cpp 759<\/li>\n<\/ul>\n<p>  <\/p>\n<h2 id=\"loop-error\">Loop error<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v560\/\">V560<\/a> A part of conditional expression is always true: i != 1. PPUTranslator.cpp 4252<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">void PPUTranslator::MTFSF(ppu_opcode_t op) {   const auto value = GetFpr(op.frb, 32, true);    for (u32 i = 16; i &lt; 20; i++)   {     if (i != 1 &amp;&amp; i != 2 &amp;&amp; (op.flm &amp; (128 >> (i \/ 4))) != 0)     {       SetFPSCRBit(i, Trunc(m_ir->CreateLShr(value, i ^ 31),                   GetType&lt;bool>()), false);     }   }    if (op.rc) SetCrFieldFPCC(1); }<\/code><\/pre>\n<p>  <\/p>\n<p>The for-loop above works with numbers from 16 to 20, which means that the condition of the if-block inside the loop is never met and the <em>i<\/em> variable value is never evaluated against 1 and 2. Maybe someone refactored this code and forgot to change the indexes to the correct ones.<\/p>\n<p>  <\/p>\n<h2 id=\"pointer-dereferencing-before-check\">Pointer dereferencing before check<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v595\/\">V595<\/a> The &#8216;cached_dest&#8217; pointer was utilized before it was verified against nullptr. Check lines: 3059, 3064. texture_cache.h 3059<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">template &lt;typename surface_store_type, typename blitter_type, typename ...Args> blit_op_result upload_scaled_image(....) {   \/\/ ....    if (!use_null_region) [[likely]]   {     \/\/ Do preliminary analysis     typeless_info.analyse();      blitter.scale_image(cmd, vram_texture, dest_texture, src_area, dst_area,                         interpolate, typeless_info);   }   else   {     cached_dest->dma_transfer(cmd, vram_texture, src_area, \/\/ &lt;=                               dst_range, dst.pitch);   }    blit_op_result result = true;    if (cached_dest) \/\/ &lt;=   {     result.real_dst_address = cached_dest->get_section_base();     result.real_dst_size = cached_dest->get_section_size();   }   else   {     result.real_dst_address = dst_base_address;     result.real_dst_size = dst.pitch * dst_dimensions.height;   }    return result; }<\/code><\/pre>\n<p>  <\/p>\n<p>We can see one more frequent pattern here \u2014 first, a pointer is used, and only then is it checked. Again, someone could have unknowingly created this error when modifying the code.<\/p>\n<p>  <\/p>\n<h2 id=\"checking-new-result-for-null\">Checking &#8216;new&#8217; result for null<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v668\/\">V668<\/a> There is no sense in testing the &#8216;movie&#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. movie_item.h 56<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">void init_movie(const QString&amp; path) {   if (path.isEmpty() || !m_icon_callback) return;    if (QMovie* movie = new QMovie(path); movie &amp;&amp; movie->isValid())   {     m_movie = movie;   }   else   {     delete movie;     return;   }    QObject::connect(m_movie, &amp;QMovie::frameChanged, m_movie, m_icon_callback); }<\/code><\/pre>\n<p>  <\/p>\n<p>Checking for nullptr is pointless here: if the <em>new<\/em> call causes an error, the <em>std::bad_alloc<\/em> exception is thrown. If there&#8217;s no need to throw an exception, one can use the <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/memory\/new\/nothrow\">std::nothrow<\/a> construction \u2014 in this case the null pointer will be returned.<\/p>\n<p>  <\/p>\n<p>Here are some more locations with this error:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/pvs-studio.com\/en\/w\/v668\/\">V668<\/a> There is no sense in testing the &#8216;m_render_creator&#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. emu_settings.cpp 75<\/li>\n<li><a href=\"https:\/\/pvs-studio.com\/en\/w\/v668\/\">V668<\/a> There is no sense in testing the &#8216;trophy_slider_label&#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. trophy_manager_dialog.cpp 216<\/li>\n<\/ul>\n<p>  <\/p>\n<h2 id=\"memory-leak\">Memory leak<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v773\/\">V773<\/a> The function was exited without releasing the &#8216;buffer&#8217; pointer. A memory leak is possible. rsx_debugger.cpp 380<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">u8* convert_to_QImage_buffer(rsx::surface_color_format format,                              std::span&lt;const std::byte> orig_buffer,                              usz width, usz height) noexcept {   u8* buffer = static_cast&lt;u8*>(std::malloc(width * height * 4));   if (!buffer || width == 0 || height == 0)   {     return nullptr;   }   for (u32 i = 0; i &lt; width * height; i++)   {     \/\/ depending on original buffer, the colors may need to be reversed     const auto &amp;colors = get_value(orig_buffer, format, i);     buffer[0 + i * 4] = colors[0];     buffer[1 + i * 4] = colors[1];     buffer[2 + i * 4] = colors[2];     buffer[3 + i * 4] = 255;   }   return buffer; }<\/code><\/pre>\n<p>  <\/p>\n<p>At the beginning, the function uses <em>malloc<\/em> to allocate memory. If <em>nullptr<\/em> is returned, the function exits. So far so good. Then the <em>width<\/em> and <em>height<\/em> parameters are checked \u2014 this takes place after the memory has been allocated. In case of success, the function also returns <em>nullptr<\/em>. Yes, if these variables equal zero, malloc returns 0 bytes. However, the standard states that in this case the function may return either <em>nullptr<\/em> or a valid pointer that cannot be dereferenced. But either way, it needs to be freed. Besides, <em>free<\/em> is also capable of accepting a null pointer. So the fix may look like this:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">if (!buffer || width == 0 || height == 0) {   std::free(buffer)   return nullptr; }<\/code><\/pre>\n<p>  <\/p>\n<p>Alternatively, you can remove checks for 0 altogether \u2014 the loop will not be executed in this case:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">if (!buffer) {   return nullptr; } for (u32 i = 0; i &lt; width * height; i++) {   \/\/ .... } return buffer;<\/code><\/pre>\n<p>  <\/p>\n<h2 id=\"incorrect-size-check\">Incorrect size check<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v557\/\">V557<\/a> Array overrun is possible. The &#8216;pad&#8217; index is pointing beyond array bound. pad_thread.cpp 191<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">void pad_thread::SetRumble(const u32 pad, u8 largeMotor, bool smallMotor) {   if (pad > m_pads.size())     return;    if (m_pads[pad]->m_vibrateMotors.size() >= 2)   {     m_pads[pad]->m_vibrateMotors[0].m_value = largeMotor;     m_pads[pad]->m_vibrateMotors[1].m_value = smallMotor ? 255 : 0;   } }<\/code><\/pre>\n<p>  <\/p>\n<p>The code above uses the > operator instead of >= to check input data. As a result, the <em>pad<\/em> value can be equal to the <em>m_pads<\/em> container size. This may cause an overflow when the container is accessed the next time.<\/p>\n<p>  <\/p>\n<h2 id=\"shift-in-wrong-direction\">Shift in wrong direction<\/h2>\n<p>  <\/p>\n<p><a href=\"https:\/\/pvs-studio.com\/en\/w\/v547\/\">V547<\/a> Expression &#8216;current_version &lt; threshold_version&#8217; is always false. Unsigned type value is never &lt; 0. device.cpp 91<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">void physical_device::create(VkInstance context,                              VkPhysicalDevice pdev,                              bool allow_extensions) {   else if (get_driver_vendor() == driver_vendor::NVIDIA)   { #ifdef _WIN32     \/\/ SPIRV bugs were fixed in 452.28 for windows     const u32 threshold_version = (452u >> 22) | (28 >> 14); #else     \/\/ SPIRV bugs were fixed in 450.56 for linux\/BSD     const u32 threshold_version = (450u >> 22) | (56 >> 14); #endif     \/\/ Clear patch and revision fields     const auto current_version = props.driverVersion &amp; ~0x3fffu;     if (current_version &lt; threshold_version)     {       rsx_log.error(....);     }   } }<\/code><\/pre>\n<p>  <\/p>\n<p>The <em>threshold_version<\/em> constant is always 0, because the right shift is used instead of the left shift. The right shift is equivalent to dividing by a power of two \u2014 in our case, by 2^22 and 2^14 respectively. It is obvious that the values from the expressions above are less than these powers. This means the result is always zero.<\/p>\n<p>  <\/p>\n<p>Looks like someone copied this snippet from the code that decoded version values and forgot to change the operators.<\/p>\n<p>  <\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>  <\/p>\n<p>The analyzer checked the project and found various errors: from traditional ones \u2014 like typos \u2014 to more intricate issues like logical errors caused by the fact that some parts of the code were not tested. We hope that this check will help fix a couple of bugs. We also hope the emulator&#8217;s developers keep up the great work supporting games and we wish their emulator excellent performance. Got curious? You can download the PVS-Studio analyzer&#8217;s <a href=\"https:\/\/pvs-studio.com\/pvs-studio\/try-free\/?utm_source=habr&amp;utm_medium=articles&amp;utm_content=rpcs3&amp;utm_term=link_try-free\">trial version<\/a> and see what errors it finds in your code. And if you are developing an open-source game or project, we invite you to consider our <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/0614\/\">free license<\/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\/589269\/\"> https:\/\/habr.com\/ru\/articles\/589269\/<\/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>RPCS3 is an interesting project that emulates the PS3 console. It is actively evolving. Recently we heard the news that the emulator learned how run all the games from the console&#8217;s catalog. That&#8217;s a good excuse to analyze the project. We&#8217;ll see which errors remained after new fixes were added to the project.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/post_images\/c7d\/156\/f1c\/c7d156f1c49a1ec607de9aca78a1e872.png\" alt=\"0886_rpcs3\/image1.png\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/post_images\/c7d\/156\/f1c\/c7d156f1c49a1ec607de9aca78a1e872.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-408794","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/408794","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=408794"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/408794\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=408794"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=408794"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=408794"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}