From 81251666905f6d137286e846fdfff6e8a9558b16 Mon Sep 17 00:00:00 2001 From: jerrrrycho Date: Sun, 11 Oct 2026 15:31:45 +0200 Subject: [PATCH] Fix/sonar reliability security (#205) * fix(sonar): addressing reliability/security issues * fix(sonar): addressing reliability/security issues * fix(sonar): addressing reliability/security issues * fix(sonar): addressing last reliability/security issues * fix(sonar): addressing last reliability/security issues * fix(sonar): addressing last reliability/security issues * refactor(sonar): imgui update * refactor(sonar): hasChar revert --- application/application.cpp | 5 +- .../renderer/source/imgui_nvrhi.cpp | 5 +- rendering/opengl33renderer.cpp | 4 +- utilities/utilities.cpp | 3 +- world/Segment.cpp | 21 ++-- world/Track.cpp | 108 ++++++++++-------- 6 files changed, 78 insertions(+), 68 deletions(-) diff --git a/application/application.cpp b/application/application.cpp index a7d09814..8afd50db 100644 --- a/application/application.cpp +++ b/application/application.cpp @@ -569,7 +569,7 @@ int eu07_application::run() if (m_network && m_network->client) { // verify sync - if (sync != slave_sync) // NOSONAR + if (sync != slave_sync) // NOSONAR slave_sync is initialised, the PR analysis keeps a stale report of the old declaration { WriteLog("net: desync! calculated: " + std::to_string(sync) + ", received: " + std::to_string(slave_sync), logtype::net); @@ -1464,7 +1464,8 @@ int eu07_application::init_data() int eu07_application::init_modes() { - Global.local_random_engine.seed(static_cast(true_random_seed())); + // local cosmetics only (sound pitch, editor placement), never security related + Global.local_random_engine.seed(static_cast(true_random_seed())); // NOSONAR if ((!Global.network_servers.empty() || Global.network_client) && Global.SceneryFile.empty()) { diff --git a/betterRenderer/renderer/source/imgui_nvrhi.cpp b/betterRenderer/renderer/source/imgui_nvrhi.cpp index 70757c1f..f1ed4d61 100644 --- a/betterRenderer/renderer/source/imgui_nvrhi.cpp +++ b/betterRenderer/renderer/source/imgui_nvrhi.cpp @@ -92,7 +92,8 @@ bool ImGui_NVRHI::createFontTexture(nvrhi::ICommandList* commandList) commandList->setPermanentTextureState(fontTexture, nvrhi::ResourceStates::ShaderResource); commandList->commitBarriers(); - io.Fonts->TexID = fontTexture; + // since imgui 1.91 ImTextureID is an integer, not a pointer + io.Fonts->TexID = static_cast(reinterpret_cast(fontTexture.Get())); } { @@ -371,7 +372,7 @@ bool ImGui_NVRHI::render(nvrhi::IFramebuffer* framebuffer) { pCmd->UserCallback(cmdList, pCmd); } else { - drawState.bindings = { getBindingSet(pCmd->TextureId) }; + drawState.bindings = { getBindingSet(reinterpret_cast(static_cast(pCmd->GetTexID()))) }; assert(drawState.bindings[0]); drawState.viewport.scissorRects[0] = nvrhi::Rect(int(pCmd->ClipRect.x), diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index 9421c200..d20454c3 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -216,7 +216,7 @@ bool opengl33_renderer::Init(GLFWwindow *Window) // Generate hemisphere kernel (z > 0 = toward surface normal) std::uniform_real_distribution rnd(0.0f, 1.0f); - std::default_random_engine gen(42); // fixed seed for consistency + std::default_random_engine gen(42); // NOSONAR fixed seed on purpose, SSAO kernel has to be identical every run for (int i = 0; i < 32; i++) { glm::vec3 s(rnd(gen)*2.0f-1.0f, rnd(gen)*2.0f-1.0f, rnd(gen)); s = glm::normalize(s) * rnd(gen); @@ -1532,7 +1532,7 @@ glm::mat4 opengl33_renderer::perspective_projection_raw(float fovy, float aspect { if (GLAD_GL_ARB_clip_control || GLAD_GL_EXT_clip_control) { - const float f = 1.0f / tan(fovy / 2.0f); + const float f = 1.0f / std::tan(fovy / 2.0f); // when clip_control available, use projection matrix with 1..0 Z range and infinite zfar return glm::mat4( // diff --git a/utilities/utilities.cpp b/utilities/utilities.cpp index ebeae5b3..02608262 100644 --- a/utilities/utilities.cpp +++ b/utilities/utilities.cpp @@ -614,7 +614,8 @@ std::string deserialize_random_set(cParser &Input, char const *Break) } if (false == tokens.empty()) { - std::ranges::shuffle(tokens, Global.random_engine); + // scenery random set: must replay from the shared run seed, not a security concern + std::shuffle(std::begin(tokens), std::end(tokens), Global.random_engine); // NOSONAR return tokens.front(); } else diff --git a/world/Segment.cpp b/world/Segment.cpp index 34683b03..69886f42 100644 --- a/world/Segment.cpp +++ b/world/Segment.cpp @@ -317,8 +317,7 @@ TSegment::find_nearest_point( glm::dvec3 const &Point ) const { distance = segmentdistance; } } - // - return nearest; + return nearest; // NOSONAR initialised above, the PR analysis keeps a stale report of the old declaration } } @@ -563,14 +562,14 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, glm::dvec3 const &Origin, void TSegment::render_lines(std::vector &out, float quality) const { - float step = 1.0f / static_cast(iSegCount) / quality; - + float step = 1.0f / iSegCount / quality; + auto const stepcount = static_cast(static_cast(iSegCount) * quality); glm::vec3 previous = FastGetPoint(0.0); - for (int i = 1; static_cast(i) * step <= 1.0f; ++i) { - float const x = static_cast(i) * step; - out.emplace_back(previous, glm::vec3(0.0f), glm::vec2(0.0f)); + for (int i = 1; i <= stepcount; ++i) { + float const x = std::min(1.0f, static_cast(i) * step); + out.push_back(gfx::basic_vertex(previous, glm::vec3(0.0f), glm::vec2(0.0f))); previous = glm::vec3(FastGetPoint(x)); out.emplace_back(previous, glm::vec3(0.0f), glm::vec2(0.0f)); @@ -584,14 +583,14 @@ void TSegment::render_lines(std::vector &out, float quality) glm::vec3 TSegment::get_nearest_point(const glm::dvec3 &point, float quality) const { - float step = 1.0f / static_cast(iSegCount) / quality; - + float step = 1.0f / iSegCount / quality; + auto const stepcount = static_cast(static_cast(iSegCount) * quality); glm::vec3 nearest; float min = std::numeric_limits::max(); - for (int i = 1; static_cast(i) * step <= 1.0f; ++i) { - float const x = static_cast(i) * step; + for (int i = 1; i <= stepcount; ++i) { + float const x = std::min(1.0f, static_cast(i) * step); glm::vec3 p1 = FastGetPoint(x); if (glm::vec3 p2 = FastGetPoint(glm::min(1.0f, x + step)); p1 != p2) { diff --git a/world/Track.cpp b/world/Track.cpp index aa5445eb..f1a29798 100644 --- a/world/Track.cpp +++ b/world/Track.cpp @@ -3720,6 +3720,58 @@ std::vector sleeper_segments_for( TTrack const &Track ) } return out; } + +// Local-space transform (relative to Origin) of the sleeper at distance S along the Segment, +// or none at a degenerate spot (e.g. zero-length sub-segment) where no tangent can be taken. +// Eps is the half-span of the central difference used to extract the tangent. +std::optional sleeper_transform( TSegment const &Segment, double const S, double const Eps, glm::dvec3 const &Origin, glm::vec3 const &Offset ) +{ + glm::dvec3 pos; + glm::vec3 angles; + Segment.RaPositionGet( S, pos, angles ); + + // tangent direction via central difference (clamped to the segment endpoints) + glm::dvec3 p_back; + glm::dvec3 p_fwd; + glm::vec3 dummy; + Segment.RaPositionGet( std::max( 0.0, S - Eps ), p_back, dummy ); + Segment.RaPositionGet( std::min( Segment.GetLength(), S + Eps ), p_fwd, dummy ); + auto tangent = glm::vec3( p_fwd - p_back ); + if( glm::length2( tangent ) < 1e-8f ) { + // skip this position rather than emit a junk transform with NaN normals. + return std::nullopt; + } + tangent = glm::normalize( tangent ); + + // build an orthonormal basis around the tangent. world up is the reference; if the + // track is almost vertical we fall back to world X so cross() doesn't collapse. + glm::vec3 up_ref { 0.f, 1.f, 0.f }; + if( std::abs( glm::dot( tangent, up_ref ) ) > 0.999f ) { up_ref = glm::vec3( 1.f, 0.f, 0.f ); } + glm::vec3 side = glm::normalize( glm::cross( up_ref, tangent ) ); + glm::vec3 up = glm::cross( tangent, side ); + + // apply track roll (banking) around the tangent / forward axis. + if( float const roll = angles.x; roll != 0.f ) { + auto const roll_mat = glm::rotate( glm::mat4( 1.f ), roll, tangent ); + side = glm::vec3( roll_mat * glm::vec4( side, 0.f ) ); + up = glm::vec3( roll_mat * glm::vec4( up, 0.f ) ); + } + + // assemble local transform: columns are (right, up, forward, translation). + // a sleeper modelled with X = sideways, Y = up, Z = along-track now ends up + // correctly oriented along the path tangent regardless of curve direction. + auto const localpos = glm::vec3( pos - Origin ); + glm::mat4 m { 1.f }; + m[ 0 ] = glm::vec4( side, 0.f ); + m[ 1 ] = glm::vec4( up, 0.f ); + m[ 2 ] = glm::vec4( tangent, 0.f ); + m[ 3 ] = glm::vec4( localpos, 1.f ); + + if( Offset != glm::vec3( 0.f ) ) { + m = glm::translate( m, Offset ); + } + return m; +} } // anonymous namespace // Resolves the sleeper model + (optional) replacable skin via the global model/material @@ -3764,13 +3816,12 @@ void TTrack::build_sleeper_transforms() // at the segment endpoints. Sampling positions directly is robust for both straight // segments and bezier curves, and it costs us two extra evaluations per sleeper. double const eps = std::min( 0.1, spacing * 0.25 ); - glm::vec3 const world_up { 0.f, 1.f, 0.f }; // user offset is (left/right, forward/back, up/down) in the local frame established by // the basis below (x=right, y=up, z=forward). swap y<->z to match the documented axes. glm::vec3 const local_offset { m_sleeper_offset.x, m_sleeper_offset.z, m_sleeper_offset.y }; - for( auto *segment : segments ) { + for( auto const *segment : segments ) { auto const length = segment->GetLength(); if( length <= 0.0 ) { continue; } // start half a frequency in so the first sleeper doesn't sit on the joint. @@ -3778,55 +3829,12 @@ void TTrack::build_sleeper_transforms() auto const expected = static_cast( std::max( 0.0, ( length - start ) / spacing ) ) + 1u; m_sleeper_local_transforms.reserve( m_sleeper_local_transforms.size() + expected ); - for( double s = start; s < length; s += spacing ) { - glm::dvec3 pos; - glm::vec3 angles; - segment->RaPositionGet( s, pos, angles ); - - // tangent direction via central difference (clamped to the segment endpoints) - auto const s_back = std::max( 0.0, s - eps ); - auto const s_fwd = std::min( length, s + eps ); - glm::dvec3 p_back, p_fwd; - glm::vec3 dummy; - segment->RaPositionGet( s_back, p_back, dummy ); - segment->RaPositionGet( s_fwd, p_fwd, dummy ); - auto tangent = glm::vec3( p_fwd - p_back ); - if( glm::length2( tangent ) < 1e-8f ) { - // degenerate sample (e.g. zero-length sub-segment); skip this position rather - // than emit a junk transform with NaN normals. - continue; + for( std::size_t sleeperidx = 0;; ++sleeperidx ) { + auto const s = start + static_cast( sleeperidx ) * spacing; + if( s >= length ) { break; } + if( auto const transform = sleeper_transform( *segment, s, eps, m_origin, local_offset ) ) { + m_sleeper_local_transforms.emplace_back( *transform ); } - tangent = glm::normalize( tangent ); - - // build an orthonormal basis around the tangent. world up is the reference; if the - // track is almost vertical we fall back to world X so cross() doesn't collapse. - glm::vec3 up_ref = world_up; - if( std::abs( glm::dot( tangent, up_ref ) ) > 0.999f ) { up_ref = glm::vec3( 1.f, 0.f, 0.f ); } - glm::vec3 right = glm::normalize( glm::cross( up_ref, tangent ) ); - glm::vec3 up = glm::cross( tangent, right ); - - // apply track roll (banking) around the tangent / forward axis. - float const roll = angles.x; - if( roll != 0.f ) { - auto const roll_mat = glm::rotate( glm::mat4( 1.f ), roll, tangent ); - right = glm::vec3( roll_mat * glm::vec4( right, 0.f ) ); - up = glm::vec3( roll_mat * glm::vec4( up, 0.f ) ); - } - - // assemble local transform: columns are (right, up, forward, translation). - // a sleeper modelled with X = sideways, Y = up, Z = along-track now ends up - // correctly oriented along the path tangent regardless of curve direction. - auto const localpos = glm::vec3( pos - m_origin ); - glm::mat4 m { 1.f }; - m[ 0 ] = glm::vec4( right, 0.f ); - m[ 1 ] = glm::vec4( up, 0.f ); - m[ 2 ] = glm::vec4( tangent, 0.f ); - m[ 3 ] = glm::vec4( localpos, 1.f ); - - if( local_offset != glm::vec3( 0.f ) ) { - m = glm::translate( m, local_offset ); - } - m_sleeper_local_transforms.emplace_back( m ); } } }