Incorrect Use of Autoboxing and Unboxing for Performance Critical Operations

Description

Incorrect Use of Autoboxing and Unboxing for Performance Critical Operations occurs when code uses boxed primitives, which may introduce inefficiencies into performance-critical operations. Languages like Java and C# automatically convert primitive types to corresponding wrapper classes (autoboxing) and vice versa (unboxing). While this simplifies code, using boxed primitives in performance-sensitive contexts creates significant overhead including object allocation, garbage collection pressure, and cache inefficiency.

Risk

Autoboxing in performance-critical code has significant implications. CPU resources may be consumed excessively. Memory allocation overhead increases. Garbage collection pauses may occur. Cache efficiency is reduced. Response times may be degraded. Resource exhaustion may be possible. Denial of service conditions may arise. System availability may be impacted.

Solution

Use primitive types instead of boxed types in performance-critical code. Limit boxed primitives to situations requiring typed parameters. Avoid autoboxing in loops or frequently-called methods. Use specialized collections like IntStream instead of Stream. Consider SparseArrays or ArrayMap instead of HashMap. Profile code to identify autoboxing hotspots. Review scientific computing code for primitive usage. Use static analysis tools to detect autoboxing.

Common Consequences

ImpactDetails
AvailabilityScope: Availability

DoS: Resource Consumption (CPU/Memory) - Autoboxing/unboxing in performance-critical code causes reduced performance and potential resource exhaustion, impacting system availability.

Example Code

Vulnerable Code

// Vulnerable: Using boxed primitives in performance-critical loop

public class VulnerableAutoboxing {

    // VULNERABLE: Long instead of long causes massive autoboxing
    public long vulnerableSum() {
        Long count = 0L;  // Boxed Long

        for (long i = 0; i < Integer.MAX_VALUE; i++) {
            count += i;  // Autoboxing on every iteration!
            // This creates a new Long object each time
        }

        return count;
    }

    // VULNERABLE: Using Integer in collection causes boxing on insert/retrieve
    public int vulnerableCollectionSum(List<Integer> numbers) {
        Integer sum = 0;  // Boxed Integer

        for (Integer num : numbers) {
            sum += num;  // Unbox num, add, then autobox result
        }

        return sum;
    }

    // VULNERABLE: Method signature forces autoboxing
    public Integer vulnerableAdd(Integer a, Integer b) {
        return a + b;  // Unbox both, add, autobox result
    }

    // VULNERABLE: HashMap with primitive-like keys
    public void vulnerableMap() {
        Map<Integer, String> map = new HashMap<>();

        for (int i = 0; i < 1000000; i++) {
            map.put(i, "value" + i);  // Autobox i to Integer
        }

        for (int i = 0; i < 1000000; i++) {
            String value = map.get(i);  // Autobox i to Integer
        }
    }

    // VULNERABLE: Conditional causing repeated boxing
    public void vulnerableConditional(List<Integer> numbers) {
        for (int i = 0; i < numbers.size(); i++) {
            // Comparing int to Integer causes unboxing
            if (numbers.get(i) == i) {  // Unbox for comparison
                process(numbers.get(i));  // Additional get and unbox
            }
        }
    }
}
// Vulnerable: C# autoboxing issues

public class VulnerableBoxingCSharp
{
    // VULNERABLE: Object parameter forces boxing
    public void VulnerableLog(object value)
    {
        Console.WriteLine(value.ToString());
    }

    public void VulnerableLoop()
    {
        for (int i = 0; i < 1000000; i++)
        {
            VulnerableLog(i);  // Boxing on every call!
        }
    }

    // VULNERABLE: Using ArrayList instead of List<int>
    public int VulnerableArrayList()
    {
        ArrayList list = new ArrayList();

        for (int i = 0; i < 100000; i++)
        {
            list.Add(i);  // Boxing
        }

        int sum = 0;
        foreach (object item in list)
        {
            sum += (int)item;  // Unboxing
        }

        return sum;
    }

    // VULNERABLE: Struct boxing through interface
    public interface IValue
    {
        int GetValue();
    }

    public struct ValueStruct : IValue
    {
        public int Value;
        public int GetValue() => Value;
    }

    public void VulnerableInterface()
    {
        IValue value = new ValueStruct { Value = 42 };  // Boxing!
        int result = value.GetValue();
    }
}
// Vulnerable: Kotlin nullable types cause boxing

class VulnerableKotlin {
    // VULNERABLE: Int? is boxed, Int is primitive
    fun vulnerableSum(numbers: List<Int?>): Int {
        var sum: Int? = 0  // Nullable = boxed

        for (num in numbers) {
            sum = sum!! + (num ?: 0)  // Multiple boxing operations
        }

        return sum!!
    }

    // VULNERABLE: Using java.lang.Integer explicitly
    fun vulnerableExplicit(): Long {
        var count: java.lang.Long = 0L

        for (i in 0 until Int.MAX_VALUE) {
            count += i  // Autoboxing
        }

        return count
    }
}

Fixed Code

// Fixed: Using primitive types in performance-critical code

public class SecurePrimitiveUsage {

    // FIXED: Using primitive long
    public long efficientSum() {
        long count = 0L;  // Primitive long

        for (long i = 0; i < Integer.MAX_VALUE; i++) {
            count += i;  // No autoboxing!
        }

        return count;
    }

    // FIXED: Using primitive for accumulation
    public int efficientCollectionSum(List<Integer> numbers) {
        int sum = 0;  // Primitive int

        for (int i = 0; i < numbers.size(); i++) {
            sum += numbers.get(i);  // Single unbox per element
        }

        return sum;
    }

    // FIXED: Using IntStream for better performance
    public int streamSum(List<Integer> numbers) {
        return numbers.stream()
            .mapToInt(Integer::intValue)  // Convert to IntStream
            .sum();  // Primitive operations
    }

    // FIXED: Method signature uses primitives
    public int efficientAdd(int a, int b) {
        return a + b;  // Pure primitive operation
    }

    // FIXED: Using specialized primitive collections
    public void efficientMap() {
        // Using primitive collection library (e.g., Trove, Eclipse Collections)
        // TIntObjectHashMap<String> or IntObjectHashMap<String>
        // Avoids boxing for integer keys

        // Or for Android:
        // SparseArray<String> map = new SparseArray<>();

        // Standard Java alternative: use array if keys are sequential
        String[] values = new String[1000000];
        for (int i = 0; i < values.length; i++) {
            values[i] = "value" + i;
        }
    }

    // FIXED: Efficient conditional with primitives
    public void efficientConditional(List<Integer> numbers) {
        for (int i = 0; i < numbers.size(); i++) {
            int value = numbers.get(i);  // Single unbox
            if (value == i) {
                process(value);  // Reuse unboxed value
            }
        }
    }

    // FIXED: Using primitive arrays when possible
    public int[] processEfficiently(int[] input) {
        int[] result = new int[input.length];

        for (int i = 0; i < input.length; i++) {
            result[i] = input[i] * 2;  // All primitive operations
        }

        return result;
    }
}
// Fixed: C# avoiding boxing

public class SecureBoxingCSharp
{
    // FIXED: Generic parameter avoids boxing
    public void EfficientLog<T>(T value)
    {
        Console.WriteLine(value?.ToString());
    }

    public void EfficientLoop()
    {
        for (int i = 0; i < 1000000; i++)
        {
            EfficientLog(i);  // No boxing with generic
        }
    }

    // FIXED: Using generic List<int>
    public int EfficientList()
    {
        List<int> list = new List<int>();

        for (int i = 0; i < 100000; i++)
        {
            list.Add(i);  // No boxing
        }

        int sum = 0;
        foreach (int item in list)
        {
            sum += item;  // No unboxing
        }

        return sum;
    }

    // FIXED: Using Span<T> for high-performance scenarios
    public int ProcessSpan(Span<int> data)
    {
        int sum = 0;
        for (int i = 0; i < data.Length; i++)
        {
            sum += data[i];
        }
        return sum;
    }

    // FIXED: Avoid interface boxing for structs
    public struct ValueStruct
    {
        public int Value;
        public int GetValue() => Value;
    }

    public void EfficientStruct()
    {
        ValueStruct value = new ValueStruct { Value = 42 };
        int result = value.GetValue();  // No boxing - direct call
    }

    // FIXED: Use generic constraints to avoid boxing
    public T Max<T>(T a, T b) where T : IComparable<T>
    {
        return a.CompareTo(b) > 0 ? a : b;
    }
}
// Fixed: Kotlin using non-nullable types

class SecureKotlin {
    // FIXED: Non-nullable Int is primitive
    fun efficientSum(numbers: List<Int>): Int {
        var sum = 0  // Primitive int

        for (num in numbers) {
            sum += num
        }

        return sum
    }

    // FIXED: Using primitive long
    fun efficientLong(): Long {
        var count = 0L  // Primitive long

        for (i in 0 until Int.MAX_VALUE) {
            count += i
        }

        return count
    }

    // FIXED: Using primitive arrays
    fun processArray(input: IntArray): IntArray {
        val result = IntArray(input.size)

        for (i in input.indices) {
            result[i] = input[i] * 2
        }

        return result
    }

    // FIXED: Using sequences for lazy evaluation
    fun efficientSequence(numbers: List<Int>): Int {
        return numbers.asSequence()
            .filter { it > 0 }
            .map { it * 2 }
            .sum()
    }
}

CVE Examples

Autoboxing performance issues have been identified in various applications where tight loops with boxed primitives caused denial of service or degraded performance.


  • CWE-400: Uncontrolled Resource Consumption (parent)
  • CWE-1006: Bad Coding Practices (category member)

References

  1. MITRE Corporation. "CWE-1235: Incorrect Use of Autoboxing and Unboxing for Performance Critical Operations." https://cwe.mitre.org/data/definitions/1235.html
  2. Oracle Java Documentation on Autoboxing
  3. SEI CERT Oracle Coding Standard for Java (EXP04-J)
  4. ISA/IEC 62443 Part 4-1 (Req SI-2)