Guard against a null case pointer in findSummaryCaseFromEclipseResultCase. The reload path passes dynamic_cast<RimEclipseResultCase*> of the case, which is null for Roff and GRDECL input cases, causing a null dereference in RimCase::gridFileName().
Reject null buffers and negative buffer sizes up front, returning false instead of risking a null dereference or hitting util_abort. The buffer-overflow protection from the upstream fix is already present via #14144; these two input-validation guards are the remaining delta.
Human-gated workflow that takes an unresolved crash signature from the
resinsight-system-doc registry, locates the real crash site in source,
proposes and build-verifies a minimal fix, then on approval files the OPM
issue, pushes a fix branch to the magnesj fork, opens the PR, and records
the outcome back on the signature. Uncertain cases get an explanatory note
instead of an issue or PR. This is step 3 of the weekly crash-triage flow.
Reuse RimColorLegendCollection::updateColorLegend in the two roff import
sites that previously deleted and recreated the legend bound to a result,
so views referencing the legend keep their binding on re-import.
Extract the rock color matching from createColorLegendMatchDefaultRockColors
into matchDefaultRockColors so the facies branch can pass matched colors to
updateColorLegend. Remove the now-unused
RimColorLegendCollection::createColorLegend.
Address review feedback on #14199: deleting and recreating the legend left
views bound to the old legend pointing at a deleted object. Add
RimColorLegendCollection::updateColorLegend and a matching UpdateColorLegend
scriptable method that mutate the existing custom legend (name and items) so
referring views keep their binding and are notified once. A new legend is only
created when none is registered for the result or the registered legend is a
standard legend.
set_discrete_property_category_names now performs a single atomic call instead
of delete + create + per-item appends. The empty-dict removal path still uses
DeleteColorLegend.
RimColorLegendCollection::deleteColorLegend erased the default-legend map
entry before looking the legend up, so findDefaultLegendForResult always
returned null and the old legend was never removed from m_customColorLegends.
Each set_discrete_property_category_names call for the same property left an
orphaned legend behind and appended a new one.
Look up the legend before erasing the map key, and guard against deleting a
standard legend that may have been registered as a default.
The table model shares one field editor handle per column across all rows.
When an edit ends, the view schedules the editor widget for deletion with
deleteLater(), but the handle's QPointer stays valid until the deferred
deletion runs. Clicking a cell in the same column of another row reused that
doomed widget as the new cell editor, so it was destroyed right after
opening and the user had to click again. Moving horizontally was unaffected
since a different column uses a different editor handle.
Cancel the pending deferred deletion before handing the widget out again,
and guard against a null editor widget.
The boundary conditions table required several clicks in each cell before
a value could be changed, because the table view used Qt's default edit
triggers. Add an opt-in editOnSingleClick attribute to the table view
editor that starts editing on a single click and opens combo box popups
immediately, and enable it for the BCPROP keywords table.
ecl_kw_fread_alloc() returns NULL if a keyword cannot be read, e.g. when
the backing file is truncated or corrupt. ecl_file_kw_load_kw() then
called ecl_file_kw_assert_kw() unconditionally, which dereferenced the
NULL keyword and caused a SIGSEGV.
Guard the assert and inv-map registration so they only run when the read
succeeded. The NULL keyword is propagated to the calling scope, which
already handles a missing keyword gracefully.
When a new Fault Distance defaults to all faults, the ResInsight-generated
undefined grid faults were ticked by default. Add RimFaultInView::isGeneratedFault
and filter these faults out of the default selection in both the UI feature and
the Python command. An explicit fault selection is still honored.
When a fault distance object is deleted or renamed, the generated cell
result was only emptied via clearScalarResult, leaving its name in the
GENERATED result-variable dropdown. Follow the RimGridCalculation
deletion pattern: reset any view displaying the result to None and tag
the result entry as REMOVED so it disappears from the dropdown.
Guard against empty curve definitions and time steps in
RimCorrelationPlotCollection. When the first ensemble has no FOPT
summary vector, the tornado/cross-plot curve definition vector was
empty and calling .front() on it dereferenced invalid memory,
causing a segfault in RiaSummaryCurveDefinition::summaryAddressY().
Delete the detached ensembles after rebuilding the connected editors instead of before. Updating the tree while the editors still referenced freed objects caused a use-after-free crash in PdmUiTreeItemEditor teardown. This matches the ordering used in caf::CmdDeleteItemExec.
The return value of copyPdmObjectFromRipsToCaf was discarded when copying method parameters in CallPdmObjectMethod. Check the std::expected result and return a gRPC INVALID_ARGUMENT status (with telemetry and logging) when parameter validation fails, instead of silently proceeding to execute the method.
Make RiaGrpcCommandService::assignPdmObjectValues return std::expected<void, QString> so the result of copyPdmObjectFromRipsToCaf is no longer discarded. Validation failures are now propagated up and returned to the client as a gRPC INVALID_ARGUMENT status instead of being silently ignored. This also resolves the C4834 [[nodiscard]] build warning.
Reloading a grid file frees and recreates RigEclipseCaseData. The contour map projection caches raw pointers into the case data and previously only refreshed them when reset explicitly via RimReloadCaseTools::updateAll3dViews(). Callers of RimEclipseCase::reloadEclipseGridFile() that bypass updateAll3dViews(), such as RimOpmFlowJob::onCompleted(), left the projection holding dangling pointers that crashed in generateResults/hasResultEntry on the next redraw. The projection now detects that its cached RigEclipseCaseData no longer matches the current case and rebuilds itself before the stale pointers are dereferenced.
When creating a grid statistics plot from a view result whose address is valid by name but not present in the case result cache, statistics() was indexed with UNDEFINED_SIZE_T and the CAF_ASSERT aborted. Guard the all-cells branch with hasResultEntry() so an empty histogram is produced instead.
RigMainGrid::findIntersectingCells is const and lazily builds the mutable m_cellSearchTree without synchronization, yet it is called from many multi-threaded (OpenMP) query paths. When two threads race the isNull check, both build a new tree and the cvf::ref reassignment deletes the first tree while it is still being built, leading to a use-after-free and a segfault deep in AABBTree::buildTree (BoundingBox::addValid reading a dangling leaf box).
Serialize building of the cell search tree with a mutex, both in computeCachedData (which nulls and rebuilds the tree) and in the lazy build in findIntersectingCells. The query also keeps a local reference to the tree so it stays alive if another thread rebuilds m_cellSearchTree during the read-only intersection query.
Guard against a null wellPathGeometry() when configuring the 3D well target editor for the first target of a lateral. The parent well pointer can be non-null while its geometry is not yet available, which caused a null dereference when copying wellPathPoints().
The bounding box tree is destroyed by deleteInternalNodesBottomUp, which
recursed once per tree level. For deep or unbalanced trees this overflowed
the call stack during project close (RigMainGrid destruction).
Replace the recursion with an explicit stack. Deleting an internal node does
not touch its children, so the child pointers are read before the node is
deleted and the deletion order is irrelevant. Leaf nodes remain pooled and
are not deleted individually.
The leaf-node cap that selects between the optimized and the
non-optimized cell search tree construction used cellCount(), which
only counts main grid cells and excludes LGR cells. The non-optimized
buildCellSearchTree() iterates totalCellCount() (main grid plus all LGR
cells) and creates one leaf per cell, so a grid with few main cells but
many LGR cells could be routed into that path and exhaust memory,
crashing while building the AABB tree.
Base the threshold on totalCellCount() so LGR-inflated grids are routed
to the optimized aggregating path and the leaf count stays bounded.
In Rim2dIntersectionViewCollection::syncFromExistingIntersections the
branch reusing an existing view dereferenced the RimGridView ancestor
without a null check, while the branch creating a new view already
guarded it with "if ( view )".
Intersections created without a RimGridView ancestor (for example via
the gRPC/Python API on a detached intersection collection) have a null
ancestor, causing a crash on the cellVisibilityChanged disconnect/connect
calls. Guard these calls with the same null check used in the new-view
branch and always push the existing view back into the collection.
findCellAndGridIndex and ijkFromCellIndex shared the same latent
null-pointer pattern as the context menu crash: the outer case/view was
null-checked, but a sub-accessor that can also return nullptr was
dereferenced without a guard. These are reachable from the same pick and
context-menu flow when a case is still loading, in a comparison view, or
being torn down.
- findCellAndGridIndex: guard mainGrid() and geoMechData()
- ijkFromCellIndex: guard eclipseCaseData() (matching isRadialGrid) and
femParts()
RiuViewerCommands::displayContextMenu called
eclipseView->mainGrid()->findFaultFromCellIndexAndCellFace() without
checking the result of mainGrid(). When the view's case or case data is
not available, mainGrid() returns nullptr, and the call dereferenced the
null pointer inside findFaultFromCellIndexAndCellFace (caught in the
stack trace as cvf::ref<RigFaultsPrCellAccumulator>::isNull).
Guard the call with a null check on mainGrid(), matching the existing
pattern used elsewhere in this file.
The integer slider editor cross-updated the spin box and slider without blocking signals, so a single user interaction wrote to the PDM field twice. The first (nested) write could trigger a field-changed callback that rebuilt the property panel and destroyed the spin box internal line edit, after which the second write dereferenced the dangling line edit in QAbstractSpinBox::text() and crashed.
Block the other widget signals while programmatically syncing in slotSliderValueChanged and updateSliderPosition, restoring the previous blocked state so it nests safely with configureAndUpdateUi. Add null guards in updateSliderPosition and writeValueToField.
calculateWellPipeStaticCenterline dereferenced eclipseView->eclipseCase()
without checking for null. When the view has no eclipse case (or no case
data), calling eclipseCaseData() on the null RimEclipseCase segfaulted
during a timestep frame change that redraws simulation well pipes.
Add the same null guard already used by the sibling method
RimSimWellInView::wellHeadTopBottomPosition, returning early so no pipe
geometry is produced when there is no case data.
When building the device branch layer, segments were claimed using a measured-depth-only terminator. When a lower-numbered tubing branch is located at greater measured depth than a higher-numbered branch, its device segments were absorbed into the wrong branch, extending the branch beyond its true total depth. Stop claiming device segments when the segment outflow connects to a different tubing branch.
Enable the standard tree Delete action on RimFaultDistance via
setDeletable( true ). On deletion, remove the generated FAULTDIST result
from the case in the destructor; the object is still attached to the
project tree while the destructor body runs, so the owner case is
reachable.
Replace the transient Data Analytics tree label with a real
RimDataAnalyticsCollection object that owns the case-level fault distance
collection and the well target mappings. The folder shows its analytics
objects flattened and exposes a right-click menu to create a new Fault
Distance and a new Well Target Mapping.
Migrate case-level well target mappings from older project files into the
new collection via initAfterRead; fault distance is unreleased and needs
no migration. The case accessors faultDistanceCollection() and
addWellTargetMapping() now forward to the new object, and the creation
features resolve a selected Data Analytics folder.
The Data Analytics folder is built in RimEclipseCase::defineUiTreeOrdering and
is hidden while the fault distance collection is empty. Adding the first result
only refreshed the view (or the collection itself), not the case, so the case
tree ordering was never re-run and the folder stayed hidden until an unrelated
event rebuilt the tree.
Refresh the owner case from the collection whenever items change, mirroring the
RimDataFilterCollection precedent. onItemsChanged adds the folder when the first
result appears, and onChildDeleted removes it when the last result is deleted.
Carries the "Fault Distance" rename through to the C++ class names, file
names, PDM/scriptable keywords and the Python API (add_fault_distance,
class FaultDistance). The RicNewFaultDistanceResultFeature command class
keeps its name.