Fix crash on stale cell selection when creating highlight part

RivSingleCellPartGenerator uses the grid and cell index stored in the 3D
selection item. These are plain indices, and they are not revalidated when
the case data is rebuilt behind a live selection. Both RigMainGrid::gridByIndex()
and RigGridBase::cell() guard only with CVF_ASSERT, so in release builds a stale
index reads out of bounds and the resulting garbage corner indices crash in
RigGridBase::cellCornerVertices().

Validate the grid and cell index before creating the mesh drawable.
This commit is contained in:
Magne Sjaastad committed 2026-08-07 12:21:23 +02:00
1 parent d30d5e7817
commit 842520c5e9
3 files changed
+117

No files matched your search

@@ -228,9 +228,17 @@ cvf::ref<cvf::DrawableGeo> RivSingleCellPartGenerator::createMeshDrawable()
{
if ( m_rigCaseData && m_cellIndex != cvf::UNDEFINED_SIZE_T )
{
auto mainGrid = m_rigCaseData->mainGrid();
if ( !mainGrid ) return nullptr;
if ( m_gridIndex >= mainGrid->gridCount() ) return nullptr;
auto grid = m_rigCaseData->grid( m_gridIndex );
if ( !grid ) return nullptr;
// The selection can be stale after the case data has been reloaded, and the cell index can be out of bounds
if ( m_cellIndex >= grid->cellCount() ) return nullptr;
if ( m_showLgrMeshLines && RiaPreferencesGrid::current()->radialGridMode() == RiaGridDefines::RadialGridMode::CYLINDRICAL )
{
return createMeshDrawableFromLgrGridCells();
@@ -37,6 +37,7 @@ set(SOURCE_UNITTEST_FILES
${CMAKE_CURRENT_LIST_DIR}/RivAnnotationTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RivPipeGeometryGenerator-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RivIjkIntersectionGeometryGenerator-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RivSingleCellPartGenerator-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RivTernaryScalarMapper-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/ScalarMapper-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/WellPathAsciiFileReader-Test.cpp
@@ -0,0 +1,108 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "gtest/gtest.h"
#include "RigEclipseCaseData.h"
#include "RigMainGrid.h"
#include "RigReservoirBuilder.h"
#include "RivSingleCellPartGenerator.h"
#include "cvfPart.h"
#include "cvfVector3.h"
namespace
{
//--------------------------------------------------------------------------------------------------
/// Build a regular ni x nj x nk box grid in memory (no file, no view)
//--------------------------------------------------------------------------------------------------
cvf::ref<RigEclipseCaseData> buildBoxGrid( int ni, int nj, int nk )
{
RigReservoirBuilder builder;
builder.setIJKCount( cvf::Vec3st( ni, nj, nk ) );
builder.setWorldCoordinates( cvf::Vec3d( 0.0, 0.0, 0.0 ), cvf::Vec3d( ni, nj, -nk ) );
cvf::ref<RigEclipseCaseData> caseData = new RigEclipseCaseData( nullptr );
builder.createGridsAndCells( caseData.p() );
caseData->mainGrid()->computeCachedData();
return caseData;
}
} // namespace
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RivSingleCellPartGeneratorTest, ValidCellIndexCreatesDrawable )
{
cvf::ref<RigEclipseCaseData> caseData = buildBoxGrid( 2, 3, 4 );
RivSingleCellPartGenerator partGen( caseData.p(), 0, 5, cvf::Vec3d::ZERO );
cvf::ref<cvf::Part> part = partGen.createPart( cvf::Color3f::RED );
ASSERT_TRUE( part.notNull() );
EXPECT_TRUE( part->drawable() != nullptr );
}
//--------------------------------------------------------------------------------------------------
/// A stale selection can reference a cell index no longer present in the grid
//--------------------------------------------------------------------------------------------------
TEST( RivSingleCellPartGeneratorTest, CellIndexOutOfBoundsIsIgnored )
{
cvf::ref<RigEclipseCaseData> caseData = buildBoxGrid( 2, 3, 4 );
const size_t cellCount = caseData->mainGrid()->cellCount();
for ( size_t cellIndex : { cellCount, cellCount + 1000 } )
{
RivSingleCellPartGenerator partGen( caseData.p(), 0, cellIndex, cvf::Vec3d::ZERO );
cvf::ref<cvf::Part> part = partGen.createPart( cvf::Color3f::RED );
ASSERT_TRUE( part.notNull() );
EXPECT_TRUE( part->drawable() == nullptr );
}
}
//--------------------------------------------------------------------------------------------------
/// A stale selection can reference a grid no longer present in the case
//--------------------------------------------------------------------------------------------------
TEST( RivSingleCellPartGeneratorTest, GridIndexOutOfBoundsIsIgnored )
{
cvf::ref<RigEclipseCaseData> caseData = buildBoxGrid( 2, 3, 4 );
RivSingleCellPartGenerator partGen( caseData.p(), 1, 0, cvf::Vec3d::ZERO );
cvf::ref<cvf::Part> part = partGen.createPart( cvf::Color3f::RED );
ASSERT_TRUE( part.notNull() );
EXPECT_TRUE( part->drawable() == nullptr );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RivSingleCellPartGeneratorTest, UndefinedCellIndexIsIgnored )
{
cvf::ref<RigEclipseCaseData> caseData = buildBoxGrid( 2, 3, 4 );
RivSingleCellPartGenerator partGen( caseData.p(), 0, cvf::UNDEFINED_SIZE_T, cvf::Vec3d::ZERO );
cvf::ref<cvf::Part> part = partGen.createPart( cvf::Color3f::RED );
ASSERT_TRUE( part.notNull() );
EXPECT_TRUE( part->drawable() == nullptr );
}