Skip to content

Protect uncaught_exceptions virtual table against the crash path - #5028

Open
frankgh wants to merge 1 commit into
apache:trunkfrom
frankgh:CASSANDRA-21578
Open

Protect uncaught_exceptions virtual table against the crash path#5028
frankgh wants to merge 1 commit into
apache:trunkfrom
frankgh:CASSANDRA-21578

Conversation

@frankgh

@frankgh frankgh commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Fixes three issues in ExceptionsTable, which records every uncaught exception from JVMStabilityInspector's central stability path:

  • Guard the persist(t) call so a throw can't skip inspectThrowable's disk_failure_policy / OOM die handling.
  • Drain preInitialisationBuffer under its monitor in flush() to avoid a ConcurrentModificationException that could abort node startup.
  • Bound preInitialisationBuffer so a startup exception storm (or a tool context that never flushes) can't retain stack traces without limit.

patch by Francisco Guerrero; reviewed by TBD for CASSANDRA-21578

Fixes three issues in ExceptionsTable, which records every uncaught
exception from JVMStabilityInspector's central stability path:

- Guard the persist(t) call so a throw can't skip inspectThrowable's
  disk_failure_policy / OOM die handling.
- Drain preInitialisationBuffer under its monitor in flush() to avoid a
  ConcurrentModificationException that could abort node startup.
- Bound preInitialisationBuffer so a startup exception storm (or a tool
  context that never flushes) can't retain stack traces without limit.

patch by Francisco Guerrero; reviewed by TBD for CASSANDRA-21578
// otherwise add() during iteration and trigger a ConcurrentModificationException, which would propagate out of
// setupVirtualKeyspaces() and abort node startup.
List<ExceptionRow> drained;
synchronized (preInitialisationBuffer)

@frankgh frankgh Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iteration must be synchronized for arrays wrapped with Collections.synchronizedList. Here's an excerpt from the Collections.synchronizedList javadoc:

     * It is imperative that the user manually synchronize on the returned
     * list when traversing it via {@link Iterator}, {@link Spliterator}
     * or {@link Stream}:
     * <pre>
     *  List list = Collections.synchronizedList(new ArrayList());
     *      ...
     *  synchronized (list) {
     *      Iterator i = list.iterator(); // Must be in synchronized block
     *      while (i.hasNext())
     *          foo(i.next());
     *  }
     * </pre>

// together under the list monitor so concurrent persist() calls cannot race past the cap.
synchronized (preInitialisationBuffer)
{
if (preInitialisationBuffer.size() < PRE_INITIALISATION_BUFFER_CAPACITY)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we want to avoid this growing bigger than the 1000 limit we had set above in the ctor in the original patch

ExceptionsTable.persist(t);
// Recording the exception for observability must never preempt the stability handling below (the
// disk_failure_policy / OOM "die" actions in inspectThrowable). Guard it like the StorageMetrics increment above.
try { ExceptionsTable.persist(t); } catch (Throwable ignore) { /* observability only, must not throw here */ }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should never allow a failure here prevent us from processing the exception, we follow the same pattern as line 74

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant