Checker Classcast

belongs to group Basic
Identify possibly incorrect classcasts

Frameworks supported by this checker

  • java up to 11
  • android up to API level 28
  • dotnet

Warnings generated by this checker

  • ArrayStoreWarning: the value written into an array cannot be assigned to the type of the elements of the array [ CWE704 ]
  • ClasscastGenericWarning: a classcast might be incorrect at runtime [ CWE704 ]
  • ClasscastOfFieldWarning: a classcast of a field might be incorrect at runtime [ CWE704 ]
  • ClasscastOfFormalWarning: a classcast of a formal parameter might be incorrect at runtime [ CWE704 ]
  • ClasscastOfMethodReturnWarning: a classcast of the return value of a method might be incorrect at runtime [ CWE704 ]
  • UselessClasscastWarning: a classcast is useless and can be removed from the code [ CWE398 ]

Options accepted by this checker

  • allowPredictableReturnType: consider safe the cast from predictable methods
    A predictable methods is a method which return a type predictable by software developers. It's possible use the @PredictableReturnType annotation to annotate a method as predictable
  • fullCheck: check all possible class cast problems. This usually results in many false alarms
    This analysis by default is not sound, but only checks for the most relevant cases of classcast errors. This option makes the analysis sound, but will likely generate a very large number of false alarms

Annotations understood by this checker

  • none


Description

Java checks explicit type downcasts at runtime. If they do not hold, a runtime exception is thrown. This checker verifies that casts will be satisfied at runtime, so that they will never throw an exception.

Action: For each classcast warning, verify that all types that will ever reach that program point are assignable to the type used for the cast.

.Net checks explicit type downcasts at runtime. If they do not hold, a runtime exception is thrown. This checker verifies that casts will be satisfied at runtime, so that they will never throw an exception.

Action: For each classcast warning, verify that all types that will ever reach that program point are assignable to the type used for the cast.

Android checks explicit type downcasts at runtime. If they do not hold, a runtime exception is thrown. This checker verifies that casts will be satisfied at runtime, so that they will never throw an exception.

Action: For each classcast warning, verify that all types that will ever reach that program point are assignable to the type used for the cast.

The Classcast checker with the option allowPredictableReturnType=true is able to detect methods with predictable return type and method annotated with the Julia annotation @PredictableReturnType. This option could be useful when an application with android API 25 or earlier is analyzed, because Julia considers the most commonly cast patterns as safe and does not trigger a warning. For example TextView textView = (TextView) findViewId(R.id.mytextview) is considered as safe cast. If you are not confidently of the source code or you think that some patterns could have wrong cast, please run the checker without this option.


Examples

Consider the following code:

public class TestCasts {
  private Object f;
  private Object g = "hi";

  public void m1(Object o) {
    if (((String) o).startsWith("hello"))
      System.out.println("nice to meet you");
  }

  public void m2(String s, Object o) {
    if (((String) o).startsWith("hello"))
      System.out.println("nice to meet you");
  }

  public void m3(String s, Object o) {
    if (((String) wrap(o)).startsWith("hello"))
      System.out.println("nice to meet you");
  }

  public void m4() {
    if (((String) f).startsWith("hello"))
      System.out.println("nice to meet you");
  }

  public void m5() {
    if (((String) g).startsWith("hello"))
      System.out.println("nice to meet you");
  }

  private Object wrap(Object o) {
    return o;
  }

  public void caller1() {
    m1("hi");
    m1(this);
  }

  public void caller2() {
    m2("ciao", "hello");
    m2("come stai", "how are you?");
  }

  public void caller3() {
    m3("ciao", "hello");
    m3("come stai", f = new Object());
  }
}

This checker, with an analysis run from every public entry, issues the following warnings:

TestCasts.java:6: [Classcast: ClasscastOfFormalWarning] Are you sure that formal parameter "o" can be cast from java.lang.Object into java.lang.String? An inconsistent value seems passed at line 36
TestCasts.java:11: [Classcast: ClasscastOfFormalWarning] Are you sure that formal parameter "o" can be cast from java.lang.Object into java.lang.String?

since a TestCasts instance is actually passed at line 36 and reaches line 6, where a classcast exception will be raised at runtime. At line 11, there is no program point where something which is not a string is passed for o at line 10; nevertheless, Julia issues a warning there since method m2() is public and might be called with every possible value for o, hence not necessarily with a string.

By default, this checker only checks for casts on the formal parameters of entry methods. By using the fullCheck option, this checker will apply a complete check of the code, covering all possible casts in the program. This makes the analysis sound but, in general, might lead to a large number of false alarms. In the previous example, that option will make this checker issue two more warnings:

TestCasts.java:16: [Classcast: ClasscastOfMethodReturnWarning] Are you sure that the return value of wrap can be cast from java.lang.Object into java.lang.String? An inconsistent value seems passed at line 46
TestCasts.java:21: [Classcast: ClasscastOfFieldWarning] Are you sure that field f can be cast from java.lang.Object into java.lang.String? An inconsistent value seems passed at line 46

since a java.lang.Object is passed at line 46 for o and is then returned by wrap() at line 16; moreover, that same object is written at line 46 into field f and the cast at line 21 will fail.

Checking stores into arrays

Store operations into arrays might fail at runtime if the type of the elements of the array cannot hold the value written into the array. For instance, the following code:

public class CheckArrays {
  private final Object[] arr;
  private int next;

  public CheckArrays(Object[] arr) {
    this.arr = arr;
  }

  public void add(Object o) {
    if (next < arr.length)
      arr[next++] = o;
  }

  public static void main(String[] args) {
    CheckArrays ca = new CheckArrays(new String[5]);
    ca.add(new Object());
  }
}

will run into an ArrayStoreException at line 11 at runtime, since an array of strings cannot hold a java.lang.Object. This is acknowledged by Julia as well, since this checker issues the following warning:

CheckArrays.java:11: [Classcast: ArrayStoreWarning] Are you sure that you are writing into an array that can always contain the rightvalue?

Note that this erroneous situation is often the consequence of a very permissive API, that provides public methods for both setting the array and the elements of the array, with a permissive type signature, as in the example above. Such a practice is hence discouraged.

Consider the following code:

using System;

namespace DocumentationExamples
{

    public class Classcast
    {

        private object f;
        private object g = "hi";
        public void M1(object o)
        {
            if (((string)o).StartsWith("hello"))
                Console.WriteLine("nice to meet you");
        }
        public void M2(string s, object o)
        {
            if (((string)o).StartsWith("hello"))
                Console.WriteLine("nice to meet you");
        }
        public void M3(string s, object o)
        {
            if (((string)Wrap(o)).StartsWith("hello"))
                Console.WriteLine("nice to meet you");
        }
        public void M4()
        {
            if (((string)f).StartsWith("hello"))
                Console.WriteLine("nice to meet you");
        }
        public void M5()
        {
            if (((string)g).StartsWith("hello"))
                Console.WriteLine("nice to meet you");
        }
        private object Wrap(object o)
        {
            return o;
        }
        public void Caller1()
        {
            M1("hi");
            M1(this);
        }
        public void Caller2()
        {
            M2("ciao", "hello");
            M2("come stai", "how are you?");
        }
        public void Caller3()
        {
            M3("ciao", "hello");
            M3("come stai", f = new object());
        }
    }

    public class ClasscastArrays
    {
        private readonly object[] arr;
        private int next;
        public ClasscastArrays(object[] arr)
        {
            this.arr = arr;
        }
        public void Add(object o)
        {
            if (next < arr.Length)
                arr[next++] = o; // ArrayStoreWarning
        }
        public static void Main(string[] args)
        {
            ClasscastArrays ca = new ClasscastArrays(new string[5]);
            ca.Add(new object());
        }
    }
}

This checke issues the following warnings:

DocumentationExamples.cs:13: [Classcast: ClasscastOfFormalWarning] Are you sure that formal parameter "o" can be cast from System.Object into System.String? An inconsistent value seems passed at line 43
DocumentationExamples.cs:18: [Classcast: ClasscastOfFormalWarning] Are you sure that formal parameter "o" can be cast from System.Object into System.String?

since a TestCasts instance is actually passed at line 43 and reaches line 13, where a classcast exception will be raised at runtime. At line 18, there is no program point where something which is not a string is passed for o at line 16; nevertheless, Julia issues a warning there since method M2() is public and might be called with every possible value for o, hence not necessarily with a string.

By default, this checker only checks for casts on the formal parameters of entry methods. By using the fullCheck option, this checker will apply a complete check of the code, covering all possible casts in the program. This makes the analysis sound but, in general, might lead to a large number of false alarms. In the previous example, that option will make this checker issue two more warnings:

DocumentationExamples.cs:23: [Classcast: ClasscastOfMethodReturnWarning] Are you sure that the return value of method "Wrap" can be cast from "System.Object" into "System.String"? An inconsistent value seems passed at line 53
DocumentationExamples.cs:28: [Classcast: ClasscastOfFieldWarning] Are you sure that field "f" can be cast from "System.Object" into "System.String"? An inconsistent value seems passed at line 53

since a System.Object is passed at line 53 for o and is then returned by Wrap at line 23; moreover, that same object is written at line 53 into field f and the cast at line 28 will fail.

Checking stores into arrays

Store operations into arrays might fail at runtime if the type of the elements of the array cannot hold the value written into the array. For instance, the following code:

will run into an ArrayTypeMismatchException at runtime, since an array of strings cannot hold a System.Object. This is acknowledged by Julia as well, since this checker issues the following warning:

DocumentationExamples.cs:68: [Classcast: ArrayStoreWarning] Are you sure that you are writing into an array that can always contain the rightvalue?

Note that this erroneous situation is often the consequence of a very permissive API, that provides public methods for both setting the array and the elements of the array, with a permissive type signature, as in the example above. Such a practice is hence discouraged.