spotbugs / spotbugs

SpotBugs is FindBugs' successor. A tool for static analysis to look for bugs in Java code.

Home Page:https://spotbugs.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

CT_CONSTRUCTOR_THROW: report violation for final fields

romani opened this issue · comments

https://spotbugs.readthedocs.io/en/stable/bugDescriptions.html#ct-be-wary-of-letting-constructors-throw-exceptions-ct-constructor-throw

https://wiki.sei.cmu.edu/confluence/display/java/OBJ11-J.+Be+wary+of+letting+constructors+throw+exceptions

Final field. Declaring the variable that is initialized to the object as final prevents the object from being partially initialized. The compiler produces a warning when there is a possibility that the variable's object might not be fully initialized. Declaring the variable final also guarantees initialization safety in multithreaded code. According to The Java Language Specification (JLS), §17.5, "final Field Semantics" [JLS 2015], "An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields." In other words, when a constructor executing in one thread initializes a final field to a known safe value, other threads are unable to see the preinitialized values of the object.

detected at checkstyle/checkstyle#14475 (comment)
We have class that has all fields as final, so if we can trust this article in web, it should be affected by this Finalizer attacks.

can we skip violations on classes that have all fields as final?

Example: https://github.com/checkstyle/checkstyle/blob/9349abeaed8715e3f005b8ec26660738edd25846/src/main/java/com/puppycrawl/tools/checkstyle/DefaultLogger.java#L42

My understanding is that (regardless of any final/non final fields), the object might be in an invalid state after throwing the exception. So it could be vulnerable to finalizer attacks.
For instance this code sample shows that admin might be true with x == 0:

public class ConstructorThrowTest {

	public final boolean admin;
	public final int x;

	public ConstructorThrowTest(int x) {
		admin = (x != 42);

		if (x <= 0) {
			throw new RuntimeException("x <= 0 ");
		}

		this.x = x;
	}

	public static class ConstructorThrowTestFinalizerAttack extends ConstructorThrowTest {
		public ConstructorThrowTestFinalizerAttack(int x) {
			super(x);
		}

		@Override
		protected void finalize() throws Throwable {
			System.out.println(admin + " " + x);
		}
	}

	public static void main(String[] args) throws InterruptedException {
		try {
			ConstructorThrowTestFinalizerAttack attack = new ConstructorThrowTestFinalizerAttack(-1);
		} catch (Exception e) {
		}

		System.gc();
		Thread.sleep(100000);
	}
}

This prints true 0 which shouldn't happen: the finalizer from the malicious sublcass sees the object in an invalid state.

Thanks a lot of prove