Invokable Control Element with Variadic Parameters

Description

Invokable Control Element with Variadic Parameters occurs when a function, method, or callable control element has a signature that supports a variable number of parameters or arguments (variadic parameters). While variadic functions provide flexibility, they complicate static analysis and manual code review because the actual parameters passed at each call site can vary. This makes it difficult to verify correct usage, track data flow, and identify security vulnerabilities like format string bugs or type confusion.

Risk

While primarily a code complexity issue, variadic parameters have security implications. Format string vulnerabilities (CWE-134) commonly occur in variadic functions like printf where user input can specify format specifiers. Type safety is reduced since variadic functions cannot enforce parameter types at compile time. Static analysis tools have difficulty tracking data flow through variadic functions. Code reviewers may miss security issues because the actual parameters vary per call site. Variadic functions in C/C++ can lead to undefined behavior if arguments don't match expected types.

Solution

Minimize use of variadic functions, especially in security-critical code. Use type-safe alternatives where available (e.g., variadic templates in C++, or overloaded methods). When variadic functions are necessary, validate argument counts and types at runtime. Use static analysis tools that specifically check variadic function calls. For logging and formatting, use type-safe logging frameworks. Never pass user input as format strings to variadic functions. Consider using builder patterns or option objects as alternatives to long parameter lists.

Common Consequences

ImpactDetails
OtherScope: Other

Reduce Reliability - Variadic functions complicate verification of correct usage, potentially leading to runtime errors.
OtherScope: Other

Quality Degradation - The flexibility of variadic parameters makes static analysis and code review more difficult.
ConfidentialityScope: Confidentiality, Integrity

Security Vulnerabilities - Misuse of variadic functions can lead to format string attacks or type confusion.

Example Code

Vulnerable Code

// Vulnerable: Classic variadic function with security issues
#include <stdio.h>
#include <stdarg.h>

// Vulnerable: Custom logging with variadic parameters
void vulnerable_log(const char *format, ...) {
    va_list args;
    va_start(args, format);

    // Vulnerable: No validation of format/args correspondence
    // If format has more specifiers than args, undefined behavior
    vprintf(format, args);

    va_end(args);
}

// Vulnerable usage
void process_user_input(const char *user_input) {
    // CRITICAL VULNERABILITY: User input as format string!
    vulnerable_log(user_input);  // Format string attack vector

    // Attacker input: "%s%s%s%s%s" -> crashes
    // Attacker input: "%x%x%x%x" -> leaks stack data
    // Attacker input: "%n" -> writes to memory!
}

// Vulnerable: Variadic function with no type safety
void vulnerable_sum(int count, ...) {
    va_list args;
    va_start(args, count);

    int sum = 0;
    for (int i = 0; i < count; i++) {
        // Assumes all args are int - no type checking!
        sum += va_arg(args, int);
    }

    va_end(args);
    printf("Sum: %d\n", sum);
}

// Calling with wrong types causes undefined behavior
void bad_usage() {
    vulnerable_sum(3, 1, 2.5, "three");  // Types don't match!
}
# Vulnerable: Python variadic functions with security issues
def vulnerable_query(*args, **kwargs):
    """Variadic function that builds SQL queries"""

    table = args[0] if args else kwargs.get('table')
    columns = args[1:] if len(args) > 1 else kwargs.get('columns', ['*'])

    # Vulnerable: Hard to track what's being passed
    # Each call site may pass different arguments
    query = f"SELECT {', '.join(columns)} FROM {table}"

    # If any arg contains SQL injection payload, it's executed
    return execute_query(query)


# Different call patterns make security review difficult
def usage_examples():
    vulnerable_query("users", "id", "name", "email")  # Positional
    vulnerable_query(table="users", columns=["id", "name"])  # Keyword
    vulnerable_query("users")  # Just table
    vulnerable_query(user_input, *user_columns)  # Dynamic - dangerous!
// Vulnerable: Java varargs with type safety issues
public class VulnerableFormatter {

    // Vulnerable: Variadic method accepting Object
    public static String format(String template, Object... args) {
        // No compile-time type checking on args
        // Runtime errors if args don't match template expectations

        String result = template;
        for (int i = 0; i < args.length; i++) {
            // Assumes args match placeholders in order
            result = result.replace("{" + i + "}", String.valueOf(args[i]));
        }
        return result;
    }

    // Vulnerable: SQL building with varargs
    public static String buildQuery(String table, String... columns) {
        // Each call site can pass different columns
        // Hard to track data flow for security analysis
        return "SELECT " + String.join(", ", columns) + " FROM " + table;
    }

    public void vulnerableUsage(String userInput, String[] userColumns) {
        // Security issue: user-controlled varargs
        String query = buildQuery(userInput, userColumns);
        // SQL injection possible
    }
}

Fixed Code

// Fixed: Type-safe alternatives to variadic functions

// Fixed: Structured logging instead of variadic printf
typedef struct {
    const char *message;
    const char *level;
    const char *source;
    int line;
} LogEntry;

void fixed_log(const LogEntry *entry) {
    // Type-safe: all fields have known types
    printf("[%s] %s:%d - %s\n",
           entry->level,
           entry->source,
           entry->line,
           entry->message);
}

// Fixed: Array parameter instead of variadic
void fixed_sum(const int *values, size_t count) {
    int sum = 0;
    for (size_t i = 0; i < count; i++) {
        sum += values[i];  // Type-safe: all elements are int
    }
    printf("Sum: %d\n", sum);
}

// Fixed: If variadic is needed, validate format string
void safe_log(const char *format, ...) {
    // Fixed: Never accept user input as format
    // Format must be a literal string from trusted code

    // Validate format string at compile time if possible
    #ifdef __GNUC__
    __attribute__((format(printf, 1, 2)))
    #endif

    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}

// Fixed usage: format string is never user-controlled
void fixed_process_user_input(const char *user_input) {
    // Fixed: User input is data, not format
    safe_log("User input received: %s", user_input);
}
// Fixed: C++ variadic templates for type safety

#include <iostream>
#include <sstream>

// Fixed: Variadic template with type safety
template<typename T>
void safe_log_impl(std::ostringstream& ss, T value) {
    ss << value;
}

template<typename T, typename... Args>
void safe_log_impl(std::ostringstream& ss, T first, Args... rest) {
    ss << first << " ";
    safe_log_impl(ss, rest...);
}

template<typename... Args>
void safe_log(Args... args) {
    // Type-safe: each argument type is known at compile time
    std::ostringstream ss;
    safe_log_impl(ss, args...);
    std::cout << ss.str() << std::endl;
}

// Fixed: Type-safe sum with fold expressions (C++17)
template<typename... Args>
auto safe_sum(Args... args) {
    // Compile-time type checking
    return (args + ...);
}

// Usage - compiler catches type errors
void fixed_usage() {
    safe_log("Value:", 42, "Status:", "OK");  // Type-safe
    auto sum = safe_sum(1, 2, 3, 4, 5);  // All same type enforced
    // safe_sum(1, "two", 3);  // Compile error!
}
# Fixed: Type-safe alternatives to variadic functions

from typing import List, Dict, Any, Optional
from dataclasses import dataclass

# Fixed: Explicit parameter types instead of *args
@dataclass
class QueryParams:
    table: str
    columns: List[str]
    conditions: Optional[Dict[str, Any]] = None

def fixed_query(params: QueryParams) -> str:
    """Type-safe query builder"""
    # Clear, typed parameters
    # Easy to validate and review

    # Validate inputs
    if not params.table.isidentifier():
        raise ValueError("Invalid table name")

    safe_columns = [c for c in params.columns if c.isidentifier()]

    query = f"SELECT {', '.join(safe_columns)} FROM {params.table}"
    return query


# Fixed: Use keyword arguments with defaults instead of varargs
def fixed_query_builder(
    table: str,
    columns: List[str] = None,
    where: Dict[str, Any] = None,
    limit: int = None
) -> str:
    """Explicit parameters instead of *args/**kwargs"""

    columns = columns or ['*']
    validated_columns = [c for c in columns if c.isidentifier()]

    query = f"SELECT {', '.join(validated_columns)} FROM {table}"

    if where:
        # Use parameterized queries for conditions
        pass

    if limit:
        query += f" LIMIT {int(limit)}"

    return query


# Fixed: Builder pattern for complex configurations
class QueryBuilder:
    def __init__(self, table: str):
        self._table = table
        self._columns: List[str] = []
        self._conditions: List[str] = []

    def select(self, *columns: str) -> 'QueryBuilder':
        # Varargs but each call is explicit and reviewable
        self._columns.extend(columns)
        return self

    def where(self, condition: str) -> 'QueryBuilder':
        self._conditions.append(condition)
        return self

    def build(self) -> str:
        # All components are now validated
        return f"SELECT {', '.join(self._columns)} FROM {self._table}"


# Usage is clear and auditable
query = (QueryBuilder("users")
         .select("id", "name", "email")
         .where("active = true")
         .build())
// Fixed: Type-safe alternatives in Java

import java.util.List;
import java.util.Arrays;

public class FixedFormatter {

    // Fixed: Builder pattern instead of varargs
    public static class MessageBuilder {
        private final StringBuilder message = new StringBuilder();

        public MessageBuilder append(String text) {
            message.append(text);
            return this;
        }

        public MessageBuilder append(int value) {
            message.append(value);
            return this;
        }

        public MessageBuilder append(Object value) {
            message.append(String.valueOf(value));
            return this;
        }

        public String build() {
            return message.toString();
        }
    }

    // Fixed: Explicit typed method instead of Object varargs
    public static String formatUserInfo(String name, int age, String email) {
        // All parameters have known types
        return String.format("Name: %s, Age: %d, Email: %s", name, age, email);
    }

    // Fixed: Use List instead of varargs for unknown count
    public static String buildQuery(String table, List<String> columns) {
        // Type-safe: all columns are String
        // Can validate each column
        List<String> safeColumns = columns.stream()
            .filter(c -> c.matches("^[a-zA-Z_][a-zA-Z0-9_]*$"))
            .toList();

        return "SELECT " + String.join(", ", safeColumns) + " FROM " + table;
    }

    // If varargs is necessary, wrap with validation
    public static String safeFormat(String template, Object... args) {
        // Validate count matches template placeholders
        long placeholderCount = template.chars()
            .filter(c -> c == '{')
            .count();

        if (args.length != placeholderCount) {
            throw new IllegalArgumentException(
                "Argument count doesn't match placeholder count");
        }

        String result = template;
        for (int i = 0; i < args.length; i++) {
            result = result.replace("{" + i + "}", String.valueOf(args[i]));
        }
        return result;
    }
}

CVE Examples

This CWE is marked as PROHIBITED for direct CVE mapping. However, variadic functions are closely related to format string vulnerabilities (CWE-134), which have numerous CVEs including CVE-2000-0573 and many others.


  • CWE-1120: Excessive Code Complexity (parent)
  • CWE-1226: Complexity Issues (category member)
  • CWE-134: Use of Externally-Controlled Format String (related security issue)

References

  1. MITRE Corporation. "CWE-1056: Invokable Control Element with Variadic Parameters." https://cwe.mitre.org/data/definitions/1056.html
  2. CISQ. "Automated Source Code Quality Measures."
  3. CERT C Coding Standard. "FIO30-C. Exclude user input from format strings."