Skip to content

Map KstatCtl.kc_chain as a pointer - #1741

Open
dbwiddis wants to merge 1 commit into
java-native-access:masterfrom
dbwiddis:fix-kstatctl-mapping
Open

dbwiddis wants to merge 1 commit into
java-native-access:masterfrom
dbwiddis:fix-kstatctl-mapping

Conversation

@dbwiddis

@dbwiddis dbwiddis commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

kstat_ctl_t declares kc_chain as a kstat_t pointer, but KstatCtl mapped it as an inline Kstat (ByValue) Structure, so JNA computed the structure as 200 bytes against the native 24 (32 on Solaris 11.4). Because a Structure passed by reference is written before and read after every call, each kstat_chain_update, kstat_lookup, kstat_read, and kstat_close wrote 176 bytes past the end of the block kstat_open() allocated, reverting adjacent heap to its contents at the previous call.

This PR maps kc_chain as a Pointer and adds KstatCtl.chain() to return the chain head as a Kstat, using a new Kstat(Pointer) constructor that next() now shares. Solaris 11.4's trailing kc_private field is private to libkstat and deliberately left unmapped; the structure only wraps the pointer kstat_open() returns, so the three-field mapping is a safe prefix there.

This is technically a breaking change: anyone who has mapped the previous kc_chain value will fail compilation. However, that value has never worked, it's a 64-bit timestamp, not a pointer, so the blast radius of this change is probably near-zero.

Existing native code used just the intended pointer value, ignoring JNA's claim it was a timestamp, so this effectively was a race condition if adjacent memory was used by another process: rare in libc malloc handling, common in umem handling.

Fixes #1740

kstat_ctl_t declares kc_chain as a kstat_t pointer, but KstatCtl mapped it
as an inline Kstat structure, so JNA computed the structure as 200 bytes
against the native 24 (32 on Solaris 11.4). Because a Structure passed by
reference is written before and read after every call, each
kstat_chain_update, kstat_lookup, kstat_read, and kstat_close wrote 176
bytes past the end of the block kstat_open() allocated, reverting
adjacent heap to its contents at the previous call.

Map kc_chain as a Pointer and add KstatCtl.chain() to return the chain
head as a Kstat, using a new Kstat(Pointer) constructor that next() now
shares. Solaris 11.4's trailing kc_private field is private to libkstat
and deliberately left unmapped; the structure only wraps the pointer
kstat_open() returns, so the three-field mapping is a safe prefix there.

Verified on Solaris 11.4 SPARC under libumem with UMEM_DEBUG=default:
size 24, a full walk of the 4675-entry chain, and 1000 update/lookup/read
cycles plus kstat_close with no allocator errors.

Fixes java-native-access#1740

This branch has not been deployed

No deployments
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.

Incorrect mapping of c.s.j.platform.unix.solaris.LibKstat.KstatCtl

1 participant