Ian Rogers wrote:
Jeroen Frijters wrote:
This is not true. Once an object becomes "finalizer reachable" the
finalizer can run, but that doesn't mean all references are gone. Any
object with a finalizer can still have references to objects that
have already been finalized. In its finalizer it will be able to
resurrect these objects. This is a well known attack vector and the
reason that PhantomReferences were introduced to do post-mortem cleanup.
In normal use it would be so hard to do this, but you're right that in
theory someone could try to use a thread local after its been
finalized in their own finalizer using the finalizer I wrote. I've
changed the patch as you suggested to make the localIndex an invalid
value after finalization to prevent this attack. It's unfortunate as
the change stops localIndex being a prime candidate for final field
chasing or caching in a register. This seems better than using a
phantom reference and polling a queue though.
The more I think about this the less happy I am about the solution to
not make the local index final. What my hope had been was that the
locals would be something amenable to caching in a register for a
thread. The local index as a final would also be amenable to constant
propagation and access to a thread local would, in the best case, be an
indexed register load away. The problem with escaping references, in
this situation, doesn't effect the Jikes RVM. This is because all weak
references that become finalizable are queued onto the finalizer thread.
A thread local queued on the finalizer thread would only be able to look
at the thread local variables of the finalizer thread. So the scenario
of a "secret" thread running then an "untrusted" thread running and
stealing data from the "secret" via the thread local can't occur. The
"secret" thread would have to put data into the finalizer thread's
thread local for the "untrusted" thread then to steal; I can't see how
this can occur without the "secret" thread being complicit in trying to
leak data to the "untrusted" thread - if the "secret" thread is being
complicit with the "untrusted" thread then aren't all bets off?
Anyway, this design decision seems to hang off how weak references are
finalized. If they are finalized on their own finalizer thread then the
final local index design is fine and preferable imo to the volatile
design or a weak identity hash map (however cached or stream lined) - I
think an indexed register load is the shortest bit of code you could
produce for this. If finalizable objects are finalized as part of
tearing down threads then a leak is possible and for correctness the
volatile should be used. So this comes down to a question of how
Classpath using VMs are finalizing objects? One simple idea is to palm
off the implementation decision to the VMThread and make the reference
implementation use a volatile - this way the Jikes RVM, I believe, can
get its optimal code sequence. Currently the whole issue of caching
these values in registers for the Jikes RVM is a mute one as
optimizations to reduce redundant loads are disabled.
Thanks,
Ian