Skip to content

Singleton implementation pitfalls

In this blog I want to show implementation pitfalls of the singleton pattern and show you why enums are the better singletons.

The singleton pattern is widely known and discussed a lot of times. Developers often prefer to implement a singleton using a final static field. But this approach has some pitfalls or rather malicious code can compromise the singleton pattern. In the next sections I will show you the possible attacks on singleton implementations that compromise the pattern. Maybe you will use this techniques to implement a kind of workaround some day, but I hope you will not.

Static Final Field Singleton

The purpose of a singleton is that only 1 instance of a class will ever exist. To ensure this the first thought that will come in your mind is to use a constant (static final field) and a private constructor. Thus the widely-used singleton implementation is

public class ConstantSingleton1 {
    public static final ConstantSingleton1 INSTANCE = new ConstantSingleton1();
    private ConstantSingleton1() {}
}

Reflection bypass

Don’t think that a declaring a constant and making a constructor privat is enough to ensure that only one instance will ever exist. If malicious code uses reflection it can create another instance of the class.

System.out.println("Well, you think your ConstantSingleton1 is really a singleton");
System.out.println("The id of the INSTANCE object is " + System.identityHashCode(ConstantSingleton1.INSTANCE));

System.out.println("I will create another instance using reflection");
Constructor<ConstantSingleton1> privateConstructor = ConstantSingleton1.class.getDeclaredConstructor();
privateConstructor.setAccessible(true);
ConstantSingleton1 newInstance = privateConstructor.newInstance();

System.out.println("Gotcha! Just created a new instance using relfection. id = "
						+ System.identityHashCode(newInstance));

The output will be something like this:

Well, you think your ConstantSingleton1 is really a singleton
The id of the INSTANCE object is 1202461407
I will create another instance using reflection
Gotcha! Just created a new instance using relfection. id = 1947749179

Reflection bypass prevention by caller check

We can prevent this if we implement a caller check in the constructor so that only the class initializer of ConstantSingleton1 can instantiate it.

public class ConstantSingleton2 {
    public static final ConstantSingleton2 INSTANCE = new ConstantSingleton2();

    private static boolean isCallerStaticInitializer() {
        StackTraceElement[] stackTrace = new Exception().getStackTrace();
        StackTraceElement shouldBeClinit = stackTrace[2];
        return "<clinit>".equals(shouldBeClinit.getMethodName())
	        && ConstantSingleton2.class.getName().equals(shouldBeClinit.getClassName());
    }

    private ConstantSingleton2() {
        if (!isCallerStaticInitializer()) {
            throw new RuntimeException("You are not allowed to instantiate a singleton using reflection");
        }
    }
}

This will make the ConstantSingleton2 really a singleton. But maybe your singleton also implements Serializable. Then malicious code has another option to compromise the singleton pattern.

Serialization bypass

Even we implemented a reflection prevention there are still options for malicious code to create a new instance if the singleton is also Serializable.

public class ConstantSingleton3 implements Serializable {

    private static final long serialVersionUID = -4092252413936943233L;
    public static final ConstantSingleton3 INSTANCE = new ConstantSingleton3();

    private static boolean isCallerIsStaticInitializer() {
        StackTraceElement[] stackTrace = new Exception().getStackTrace();
        StackTraceElement shouldBeClinit = stackTrace[2];
        return "<clinit>".equals(shouldBeClinit.getMethodName()) 
                    && ConstantSingleton3.class.getName().equals(shouldBeClinit.getClassName());
    }

    private ConstantSingleton3() {
        if (!isCallerIsStaticInitializer()) {
             throw new RuntimeException("You are not allowed to instantiate a singleton using reflection");
        }
    }
}

Malicious code can serialize/deserialize the singleton. Since the deserialization mechanism only create a new object but does not execute the constructor (or to be more precise initializer) code the reflection prevention does not work.

System.out.println("Well, you think your ConstantSingleton3 is really a singleton");
System.out.println("The id of the INSTANCE object is "
				+ System.identityHashCode(ConstantSingleton3.INSTANCE));

System.out.println("I will create another instance using reflection");
Constructor<ConstantSingleton3> privateConstructor = ConstantSingleton3.class.getDeclaredConstructor();
privateConstructor.setAccessible(true);
try {
    privateConstructor.newInstance();
} catch (InvocationTargetException e) {
    Throwable targetException = e.getTargetException();
    System.out.println("D'oh! An exception prevented me from creating a new instance: " + targetException.getMessage());
}

System.out.println("Hmm, I will try one more option - Serialisation");
ConstantSingleton3 clone = SerializationUtils.clone(ConstantSingleton3.INSTANCE;
System.out.println("Gotcha! I created a clone using serialization. id = " + System.identityHashCode(clone));

The output will be something like this:

Well, you think your ConstantSingleton3 is really a singleton
The id of the INSTANCE object is 1644783865
I will create another instance using reflection
D'oh! An exception prevented me from creating a new instance: You are not allowed to instantiate a singleton using reflection
Hmm, I will try one more option - Serialisation
Gotcha! I created a clone using serialization. id = 2070623242

Serialization bypass prevention by readResolve

As you can see the malicious code could create a new instance using serialization. But hopefully the java serialization mechanism provides the tools to prevent this.

To prevent the serialization bypass one can implement a private Object readResolve() method that will return the already existent singleton instance on deserialization every time.

public class ConstantSingleton4 implements Serializable {

    private static final long serialVersionUID = -4092252413936943233L;
    public static final ConstantSingleton3 INSTANCE = new ConstantSingleton3();

    private static boolean isCallerIsStaticInitializer() {
        StackTraceElement[] stackTrace = new Exception().getStackTrace();
        StackTraceElement shouldBeClinit = stackTrace[2];
        return "<clinit>".equals(shouldBeClinit.getMethodName()) 
                    && ConstantSingleton3.class.getName().equals(shouldBeClinit.getClassName());
    }

    private ConstantSingleton3() {
        if (!isCallerIsStaticInitializer()) {
             throw new RuntimeException("You are not allowed to instantiate a singleton using reflection");
        }
    }
	
    private Object readResolve() {
        return INSTANCE;
    }
}

Now malicious code has no chance.

System.out.println("Well, you think your ConstantSingleton3 is really a singleton");
System.out.println("The id of the INSTANCE object is "
				+ System.identityHashCode(ConstantSingleton3.INSTANCE));

System.out.println("I will create another instance using reflection");
Constructor<ConstantSingleton3> privateConstructor = ConstantSingleton3.class.getDeclaredConstructor();
privateConstructor.setAccessible(true);
try {
    privateConstructor.newInstance();
} catch (InvocationTargetException e) {
    Throwable targetException = e.getTargetException();
    System.out.println("D'oh! An exception prevented me from creating a new instance: " + targetException.getMessage());
}

System.out.println("Hmm, I will try one more option - Serialisation");
ConstantSingleton3 clone = SerializationUtils.clone(ConstantSingleton3.INSTANCE;
System.out.println("D'oh! Even serialization did not work. id = " + System.identityHashCode(clone));
System.out.println("I give up");

The output will be something like this:

Well, you think your ConstantSingleton4 is really a singleton
The id of the INSTANCE object is 1472914871
I will create another instance using reflection
D'oh! An exception prevented me from creating a new instance: You are not allowed to instantiate a singleton using reflection
Hmm, I will try one more option - Serialisation
D'oh! Even serialization did not work. id = 1472914871
I will give up

Static Final Field Conclusion

As you could see you have to do a lot to keep your singletons really singletons and you have even more to do if they are Serializable. So you might ask yourself if there is a better and safer way to implement a real singleton that needs less code.

Fortunately there is a better way since Java 1.5 – use enums.

Enum Singleton

Java 1.5 introduced enums and they are great for implementing real singletons, because the nature of an enum itself makes the singleton a real singleton. By definition there is always only 1 instance of an enum instance and you don’t have any options to bypass this.

So with enums you would implement a singleton like this

public enum EnumSingleton {
    INSTANCE;
}

That’s all it’s easy isn’t it.

Malicious code can not use reflection nor serialization to create a new instance, because java will prevent it. So if malicious code tries this:

System.out.println("Well, you think your EnumSingleton is really a singleton");

System.out.println("The id of the INSTANCE object is "
				+ System.identityHashCode(EnumSingleton.INSTANCE));

System.out.println("I will create another instance using reflection");
Constructor<EnumSingleton> privateConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
privateConstructor.setAccessible(true);
try {
    privateConstructor.newInstance();
} catch (IllegalArgumentException e) {
    System.out.println("D'oh! An exception prevented me from creating a new instance: "
	+ e.getMessage());
}

System.out.println("Hmm, I will try one more option - Serialisation");

EnumSingleton clone = SerializationUtils.clone(EnumSingleton.INSTANCE);
System.out.println("D'oh! Even serialization did not work. id = "
		+ System.identityHashCode(clone));

System.out.println("I give up");

The output will be

Well, you think your EnumSingleton is really a singleton
The id of the INSTANCE object is 1236237928
I will create another instance using reflection
D'oh! An exception prevented me from creating a new instance: Cannot reflectively create enum objects
Hmm, I will try one more option - Serialisation
D'oh! Even serialization did not work. id = 1236237928
I give up

Conclusion

If you want to implement a real singleton and you can use Java 1.5 or above you should use enums to do it. They need less code and are safe.

Example source code available at https://github.com/link-intersystems/blog/tree/master/singleton-implementation-pitfalls

Leave a Reply

Your email address will not be published. Required fields are marked *

 

GDPR Cookie Consent with Real Cookie Banner