In java.lang.SecurityManager
, there is a boolean field called initialized.
public class SecurityManager {
/*
* Have we been initialized. Effective against finalizer attacks.
*/
private boolean initialized = false;
//some code
/**
* Constructs a new <code>SecurityManager</code>.
*
* <p> If there is a security manager already installed, this method first
* calls the security manager's <code>checkPermission</code> method
* with the <code>RuntimePermission("createSecurityManager")</code>
* permission to ensure the calling thread has permission to create a new
* security manager.
* This may result in throwing a <code>SecurityException</code>.
*
* @exception java.lang.SecurityException if a security manager already
* exists and its <code>checkPermission</code> method
* doesn't allow creation of a new security manager.
* @see java.lang.System#getSecurityManager()
* @see #checkPermission(java.security.Permission) checkPermission
* @see java.lang.RuntimePermission
*/
public SecurityManager() {
synchronized(SecurityManager.class) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// ask the currently installed security manager if we
// can create a new one.
sm.checkPermission(new RuntimePermission
("createSecurityManager"));
}
initialized = true;
}
}
//some code
}
So apparently, the initialized field will be false
by default, but if the instantiation passed the security check and succeeds, the initialized field will be assigned as true
. There is only a comment above the initialized field, saying that it is effective against finalizer attacks, and no more description about the field is provided.
I took a search on the Internet about finalizer attacks. What I understood is that we should not depend on methods that can be overridden by untrusted code. But how is it related to the initialized field? I can still inherit java.lang.SecurityManager
, and if SecurityManager is installed but accessing private fields via reflection is allowed, the initialized field should be able to be edited. So how is it effective against finalizer attacks?
That's an older protection technique: https://www.ibm.com/developerworks/java/library/j-fv/j-fv-pdf.pdf
In a nutshell - finalizer attacks are when you override the finalize()
method of an object, which serves as the destructor that the GC will call to free up native resources. But once you subclass, or override it with reflection - invariants/"promises" of the original code no longer hold.
How to avoid the attack
Until the third edition of the Java Language Specification (JLS) was implemented in Java SE 6, the only ways to avoid the attack — using an
initialized
flag, prohibiting subclassing, or creating afinal
finalizer — were unsatisfactory solutions.Using an initialized flag:
One way to avoid the attack is to use an
initialized
flag, which is set to true once an object has been correctly created. Every method in the class first checks to see ifinitialized
is set and throws an exception if it is not. This kind of coding is tiresome to write, is easy to omit by accident, and does not stop an attacker from subclassing the method.