Skip to content

Check ignored error status of I/O calls (CodeQL java/ignored-error-status-of-call)#233

Open
vharseko wants to merge 3 commits into
OpenIdentityPlatform:masterfrom
vharseko:features/codeql-ignored-return
Open

Check ignored error status of I/O calls (CodeQL java/ignored-error-status-of-call)#233
vharseko wants to merge 3 commits into
OpenIdentityPlatform:masterfrom
vharseko:features/codeql-ignored-return

Conversation

@vharseko

@vharseko vharseko commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

Resolves all nine open java/ignored-error-status-of-call CodeQL alerts. Each
site threw away the result of a stream read/skip or of File.renameTo, so a
short read, an incomplete skip, or a failed rename went unnoticed.

File Call Fix
StreamLoader.java (139, 141) DataInputStream.read(buf, off, len) for record key/value switch to readFully
Value.java (4894, 4899) read inside readFully overrides check the count, throw EOFException on a short read
IOMeter.java (455) InputStream.skip in the dump main loop until fully skipped
BackupTask.java (263) File.renameTo throw IOException when it returns false
WDSSO.java (288, 326) ByteArrayInputStream.read for SPNEGO / Kerberos OID require a full read before matching the OID
DerValue.java (91) ByteArrayInputStream.read for DER content truncate to the bytes actually read

Why it is safe

For every well-formed input these calls already transferred the full amount:

  • StreamLoader reads from a DataInputStream; readFully reads exactly the
    requested bytes or throws EOFException — for a complete stream the byte
    count is identical to before, but a short read (buffered / socket source) can
    no longer deliver a half-filled key or value.
  • Value.ValueObjectInputStream.read already transfers exactly length bytes
    or throws IOException, so the added != length guard only documents and
    enforces the existing DataInput.readFully contract.
  • IOMeter.main is a diagnostic dumper; the skip loop reaches the same offset,
    just correctly when skip returns fewer bytes than requested.
  • BackupTask.rename only reaches the new throw when renameTo actually
    fails — previously that failure was reported as success.
  • In WDSSO/DerValue a valid token fills the OID / DER buffers completely, so
    the guard is true and the comparison result is unchanged; a truncated
    token now fails closed instead of being compared against zero-filled trailing
    bytes.

So the change is behaviour-preserving for all existing (valid) inputs and only
surfaces genuinely truncated or failed I/O.

Copyright

Adds the missing Portions Copyrighted 2026 3A Systems, LLC header line to
StreamLoader.java, BackupTask.java, WDSSO.java and DerValue.java
(Value.java and IOMeter.java already carry it). The @Checkstyle:ignoreFor
count in the two ForgeRock CDDL headers is bumped by one to keep covering the
extended header.

Testing

mvn -o -pl persistit/core,commons/auth-filters/authn-filter/jaspi-modules/iwa-module -am compile — BUILD SUCCESS.

…atus-of-call)

Nine call sites discarded the return value of a stream read/skip or of
File.renameTo, so a short read, an incomplete skip, or a failed rename passed
silently. Consume and act on each result:

  - StreamLoader.next: read the record key/value with DataInputStream.readFully
    instead of read(), so a short read on a buffered/socket stream can no longer
    hand a partially filled key or value to the import handler (latent
    corruption bug).
  - Value.ValueObjectInputStream.readFully: honour the DataInput.readFully
    contract - the local read() transfers exactly the requested length or
    throws, so a short count now raises EOFException.
  - IOMeter.main: loop until the requested byte count is fully skipped, since
    InputStream.skip may skip fewer bytes than asked.
  - BackupTask.rename: throw IOException when File.renameTo returns false rather
    than reporting success for a rename that did not happen.
  - WDSSO.parseToken and DerValue.init: use the byte count returned by
    ByteArrayInputStream.read so a truncated SPNEGO/Kerberos token or DER value
    fails closed instead of being compared against uninitialised trailing bytes.

For every well-formed input the reads/skips/renames already completed fully, so
behaviour is unchanged for existing callers; only truncated or failing I/O is
now surfaced.

Also add the missing 3A Systems Portions header line to StreamLoader.java,
BackupTask.java, WDSSO.java and DerValue.java (Value.java and IOMeter.java
already carry it).
@vharseko vharseko added bug java codeql CodeQL static-analysis findings security Security fixes and CVE remediation labels Jul 8, 2026
@vharseko vharseko requested a review from maximthomas July 8, 2026 13:05
@vharseko vharseko removed the java label Jul 8, 2026
vharseko added 2 commits July 9, 2026 12:03
The test builds a three-transaction deadlock cycle (ts1 <- ts3 <- ts2 <- ts1)
and assumed a specific thread would win the deadlock-detection race: that t3
(which closes the cycle) reports UNCOMMITTED and that t2 then clears to 0 once
ts2 aborts. But deadlock detection is distributed -- every waiter re-checks for
a cycle every SHORT_TIMEOUT (10 ms) -- so any transaction on the cycle may
report the deadlock, and more than one may do so before the cycle is broken.
Under load a second thread detected the deadlock too, so result2 was
UNCOMMITTED instead of 0 (seen on ubuntu-latest JDK 17,
expected:<0> but was:<9223372036854775807>).

Assert the invariants that always hold instead of pinning the outcome to a
particular thread: the deadlock is detected by at least one participant, ts3's
wait on ts2 ends by either detecting the deadlock or observing ts2's abort, and
each waiter blocks until the cycle forms (~1000 ms).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug codeql CodeQL static-analysis findings security Security fixes and CVE remediation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants