[sec/first] Address security review findings across Chronicle-Core#846
Draft
peter-lawrey wants to merge 38 commits into
Draft
[sec/first] Address security review findings across Chronicle-Core#846peter-lawrey wants to merge 38 commits into
peter-lawrey wants to merge 38 commits into
Conversation
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



This PR consolidates the Chronicle-Core results from the first deep dive security review wave.
It does two things in parallel:
The largest part of the change is in
UnsafeMemory, where caller-owned bounds obligations are now declared explicitly, range assertions are centralised, and many off-heap seams are annotated or reviewed rather than left implicit. The most substantive non-memory hardening is inIOTools.deleteDirWithFiles(...), which is now symlink-safe and no longer risks deleting a plain-file root.A large proportion of the diff is review metadata, but it is important metadata: in this ruleset, many bare suppressions are not “make the warning disappear” escapes. They are delegate-to-caller contracts.
Important suppression semantics
A key detail for reviewing this PR: many of the bare, non-
:silent@SuppressWarnings(...)entries in Chronicle-Core are caller-delegating, not silent waivers.Per the current
SecuritySuppressionTrackerbehaviour, a bare suppression on these rules means the callee is allowed to remain a reviewed boundary, but the caller must still prove that the use is appropriate. In other words, these suppressions are part of the contract model, not a request to ignore the issue.The caller-delegating CS rules currently used this way are:
CSRawAddressAccessCSCallerCheckedBoundsCSClassForNameInputCSSetAccessibleEscalationCSReflectiveFieldLookupCSReflectiveMethodLookupCSReflectiveConstructorLookupThat distinction matters for this PR because many reviewed sites are intentionally being converted from implicit behaviour into explicit “caller owns the proof” boundaries.
Two important exclusions:
:silentsuppressions are true silence escapes and do not delegate to the caller@CallerCheckedCopyBounds/@CallerCheckedRangeBoundsalso delegateCSCallerCheckedBounds, but they do so as annotations, not as@SuppressWarnings(...)So when this PR adds or retains a bare reviewed suppression, the intent is generally:
Why
The aim is to encode the intended contracts clearly enough that future review tooling can distinguish between:
Where the review exposed real hardening opportunities, the PR also fixes them. The main concrete fixes are:
Key changes
1. Explicit caller-owned bounds contracts for raw-memory seams
This PR introduces new contract types and helpers for low-level memory APIs:
@CallerCheckedCopyBounds@CallerCheckedRangeBoundsMemoryGuardsMemoryAegisThese are then applied across
UnsafeMemoryand related off-heap helpers so that low-level methods can explicitly state when bounds are intentionally discharged by the caller rather than re-checked inside the callee.This is the central structural change in the PR. It makes the reviewed contract visible, rather than relying on convention or broad suppressions.
In practice, this means:
@NonNegative/@Positive2. Bare reviewed suppressions now carry contractual meaning
A large part of the diff adds or retains reviewed suppressions with an explicit comment explaining why the boundary exists.
For the caller-delegating rules, these suppressions should be read as part of the API contract, not as “ignore this permanently”. Examples include:
Class.forName(...)andloadClass(...)entry points on variable namessetAccessible(...)escalation sitesThe important review lens is therefore not merely “is this suppression justified locally?”, but also “is it reasonable to leave the proof obligation with the caller here?”
3. Shared assertion helpers for debug-time memory validation
MemoryAegiscentralises assertion-time checks for:This keeps Chronicle’s existing
assert SKIP_ASSERTIONS || ...pattern, so the hot-path model stays the same in normal assertions-disabled execution, while debug/test runs get clearer range failures and more consistent validation.This is especially important in
UnsafeMemory, where the old checks were inconsistent across methods and often only asserted non-zero or non-negative inputs rather than validating the full range.4. Symlink-safe directory deletion and safer file/path handling
IOTools.deleteDirWithFiles(...)has been reworked from alistFiles()/Streamimplementation to aFiles.walkFileTree(...)implementation with explicit no-follow-link behaviour.That changes the safety model materially:
Related IO tightening includes:
Jvm.loadSystemProperties(...)now usingFiles.newInputStream(...)IOTools.urlFor(...)resolving real paths before turning them into file URLsIOTools.open(URL)closing the base stream if GZIP wrapping failsIOTools.readFile(...)reliably closing the opened stream5. Interrupt, cleanup, and lifecycle behaviour tightened
A number of cleanup and lifecycle paths were adjusted to preserve or restore interrupt status rather than accidentally consume it.
Notable examples include:
CloseableUtilsBackgroundResourceReleaserThreadLocalisedExceptionHandlerIOTools/IOToolsTestThere are also a few terminal cleanup boundaries that now catch
Throwablerather thanExceptionso cleanup can continue draining remaining work rather than aborting mid-release. These are intentionally limited to last-resort cleanup/release/logging boundaries.In addition,
AbstractCloseableReferenceCounted.initReleasedis nowvolatile, which closes a visibility race between release/close state transitions.6. Reflection/bootstrap boundaries made explicit rather than implicit
A large part of the diff turns previously implicit reflective/bootstrap behaviour into explicit, reviewed boundaries. That includes:
Class.forName(...)sitessetAccessible(...)System.err/printStackTrace()fallbacks before logging is fully availableThis does not substantially expand the use of reflection. It mostly documents and centralises already-existing reflective behaviour so that future scans can distinguish intentional internals from accidental escalation.
7. Narrower exception handling and cleaner fallback behaviour
Several places that were catching broad
ExceptionorThrowablehave been narrowed to the exceptions actually expected:IOExceptionNoSuchMethodException,IllegalAccessException,ClassNotFoundException, etc.RuntimeExceptioninstead of all checked exceptionsAt the same time, truly terminal containment points remain broad where that is the correct behaviour, for example during cleanup, release draining, chained exception logging, or startup hook execution where failure should be reported but must not stop the rest of the system from continuing.
8. Direct clock use versus provider indirection clarified
Some sites were moved to
SystemTimeProvider, while others now include explicit comments stating they must continue usingSystem.nanoTime()orSystem.currentTimeMillis()directly.This is mainly about making intent obvious:
SystemTimeProvider9. Targeted test coverage added for the new safety boundaries
The PR adds focused tests for the new helpers and for the changed deletion semantics:
MemoryAegisTestMemoryGuardsTestIOToolsTestcases covering symlinked child directories and plain-file rootsThis is important because much of the PR is contract-expression rather than feature work. The tests verify the new safety model rather than just the absence of warnings.
Impact
The practical impact of this PR is:
Critical review
This is a useful and worthwhile review pass, but there are a few places reviewers should scrutinise carefully.
1.
UnsafeMemoryis the load-bearing part of the PRMost of the
UnsafeMemoryedits are assertion helpers, annotations, and contract metadata rather than runtime logic changes, but the surface area is huge, and this is one of the highest-risk files in Chronicle-Core.The most important checks are:
@CallerCheckedCopyBounds/@CallerCheckedRangeBoundsparameter name matches the real method signature exactlyMemoryAegisassertions still allow all valid existing edge cases2. Reviewed suppressions on delegating rules still require caller scrutiny
This PR contains many reviewed suppressions, but many of them are not terminal justifications. For the seven caller-delegating rules listed above, the right review question is not only “is this local suppression reasonable?” but also:
This matters particularly for:
CSRawAddressAccessCSCallerCheckedBoundsCSClassForNameInputCSSetAccessibleEscalation3.
IOTools.deleteDirWithFiles(...)is a real semantic changeThis is the strongest concrete hardening in the PR, and it looks directionally correct.
It is also a meaningful behaviour change:
falseThat is safer, but it deserves careful review for edge cases around depth handling, broken links, and platform-specific path behaviour.
4. Some hardening is documentation-only rather than runtime-enforced
The clearest example is
UnsafeMemory.freeMemory(...), which now explicitly documents that it is not double-free safe, but still does not prevent double-free.That is honest and useful, but it is not behavioural hardening. Any expectation of runtime enforcement there would need a separate change.
5. Cleanup paths now prefer containment over propagation in a few more places
That is probably correct at terminal cleanup/release boundaries, but it slightly shifts the balance away from fail-fast and toward drain-and-log.
That should be reviewed with an eye on whether any tests or operators relied on exceptions propagating from those boundaries rather than being logged.
6. The diff is broader than the behavioural delta
The best review order is:
UnsafeMemoryMemoryAegis,MemoryGuards,CallerCheckedCopyBounds,CallerCheckedRangeBoundsIOToolsandIOToolsTestCloseableUtils,BackgroundResourceReleaser,AbstractCloseableReferenceCounted,UnsafeCloseable)That is the fastest path to the PR's true risk profile.
Draft/scope notes
This PR is opened as a draft because the branch represents one broad review-driven sweep and is easier to inspect as a single review wave than as many tiny suppressions-only changes.