Assignment to Variable without Use

Description

Assignment to Variable without Use occurs when a variable is assigned a value that is never subsequently read or used before being overwritten or going out of scope. This pattern, also called "unused assignment" or "dead store," indicates wasted computation, logic errors, or security issues. The programmer may have intended to use the value but forgot, or there may be a bug where a different variable should have been assigned. The computation to produce the value still executes, consuming resources without effect.

Risk

While unused assignments themselves don't directly cause security vulnerabilities, they strongly indicate programming errors that may have security implications. A computed security-critical value that goes unused suggests the security check was never performed. Sensitive data computed but not cleared indicates a data exposure risk. In performance-critical code, unused computations waste resources. Most importantly, dead stores often reveal logic errors—the programmer expected the value to be used but made a mistake that broke the intended functionality.

Solution

Enable compiler warnings for unused variables and dead stores (-Wunused-variable, -Wunused-but-set-variable). Use static analysis tools that detect dead stores. Investigate each instance—determine if the value should have been used, if the assignment should be removed, or if there's a bug. Remove truly unnecessary assignments. Use volatile keyword if side effects are intentional. Consider code review to catch assignments that should be used but aren't.

Common Consequences

ImpactDetails
QualityScope: Code Clarity

Dead stores make code harder to understand and maintain.
SecurityScope: Missing Checks

Security values computed but not used may indicate bypassed checks.
PerformanceScope: Wasted Computation

CPU cycles spent computing values that are never used.

Example Code + Solution Code

Vulnerable Code

// VULNERABLE: Computed value never used
int process_data_vulnerable(int* data, int size) {
    int sum = 0;
    int max = 0;  // DEAD STORE: max is never used!

    for (int i = 0; i < size; i++) {
        sum += data[i];
        if (data[i] > max) {
            max = data[i];  // DEAD STORE: max computed but never used!
        }
    }

    return sum;  // Should max also be returned?
}

// VULNERABLE: Security check result ignored
int authenticate_vulnerable(User* user, const char* password) {
    int result = check_password(user, password);
    // DEAD STORE: result is never used!

    // Bug: should have checked result!
    user->authenticated = 1;  // Always authenticates!
    return SUCCESS;
}

// VULNERABLE: Error code ignored
void process_file_vulnerable(const char* path) {
    int error = 0;  // DEAD STORE if not used

    FILE* f = fopen(path, "r");
    error = validate_file(f);
    // DEAD STORE: error is never checked!

    // Proceeds even if validation failed!
    read_contents(f);
    fclose(f);
}

// VULNERABLE: Overwritten before use
int calculate_vulnerable(int a, int b) {
    int result = a * b;  // DEAD STORE!
    result = a + b;  // Overwrites previous value

    return result;  // Multiplication was pointless
}

// VULNERABLE: Assignment in wrong scope
void search_vulnerable(int* arr, int size, int target) {
    int found = 0;

    for (int i = 0; i < size; i++) {
        if (arr[i] == target) {
            int found = 1;  // DEAD STORE: shadows outer 'found'!
            break;
        }
    }

    if (found) {  // Always 0! Inner 'found' shadowed outer
        printf("Found!\n");  // Never executes
    }
}

// VULNERABLE: Sensitive data not cleared
void process_secret_vulnerable(const char* secret) {
    char buffer[256];
    strncpy(buffer, secret, sizeof(buffer));

    // Process the secret...
    compute_hash(buffer);

    // Attempt to clear - but result unused!
    memset(buffer, 0, sizeof(buffer));  // May be optimized away!
}

// VULNERABLE: Return value computed but ignored
int validate_vulnerable(Data* data) {
    int valid;

    valid = check_format(data);
    valid = check_content(data);  // Overwrites format check!
    // DEAD STORE: First 'valid' was overwritten!

    return valid;  // Only returns content check!
}
// VULNERABLE: C++ with dead stores
class VulnerableProcessor {
public:
    int process(const std::vector<int>& data) {
        int total = 0;
        int count = 0;  // DEAD STORE: never used!

        for (int val : data) {
            total += val;
            count++;  // Computed but never used!
        }

        return total;  // Should count be used for average?
    }

    std::string format(int value) {
        std::string result = "Value: ";
        result = std::to_string(value);  // Overwrites previous!
        // DEAD STORE: "Value: " was pointless

        return result;
    }

    bool validate(const Request& req) {
        bool isValid = true;

        isValid = checkFormat(req);
        // DEAD STORE: checkFormat result immediately overwritten!

        isValid = checkContent(req);
        // Should both checks be AND'd together?

        return isValid;
    }
};

// VULNERABLE: RAII guard assigned but not used
void process_vulnerable() {
    auto guard = std::lock_guard<std::mutex>(mutex);  // OK, RAII

    int result = compute();
    result = compute_again();  // DEAD STORE: first compute unused!

    // What was compute() supposed to do?
}

// VULNERABLE: Optional value ignored
void handle_vulnerable(std::optional<int> value) {
    int result = 0;

    if (value) {
        result = *value;  // DEAD STORE if not used below!
    }

    // result is never used!
    do_something_else();
}
// VULNERABLE: JavaScript dead stores
function processVulnerable(data) {
    let sum = 0;
    let count = 0;  // Dead store - never used!

    for (let item of data) {
        sum += item;
        count++;  // Computed but never used!
    }

    return sum;
}

// VULNERABLE: Overwritten immediately
function calculateVulnerable(a, b) {
    let result = a * b;  // Dead store!
    result = a + b;  // Overwrites

    return result;
}

// VULNERABLE: Condition result unused
function validateVulnerable(input) {
    let valid = true;

    valid = checkFormat(input);
    valid = checkContent(input);  // Overwrites format check!

    return valid;  // Only content check returned!
}

// VULNERABLE: Shadow variable
function searchVulnerable(arr, target) {
    let found = false;

    for (let item of arr) {
        if (item === target) {
            let found = true;  // Shadows outer! Dead store!
            break;
        }
    }

    return found;  // Always false!
}

Fixed Code

// SAFE: Use all computed values
int process_data_safe(int* data, int size, int* out_max) {
    int sum = 0;
    int max = 0;

    for (int i = 0; i < size; i++) {
        sum += data[i];
        if (data[i] > max) {
            max = data[i];
        }
    }

    *out_max = max;  // Now max is used!
    return sum;
}

// Or remove unused computation
int process_data_simple(int* data, int size) {
    int sum = 0;

    for (int i = 0; i < size; i++) {
        sum += data[i];
    }

    return sum;  // Only compute what's needed
}

// SAFE: Use security check result
int authenticate_safe(User* user, const char* password) {
    int result = check_password(user, password);

    if (result == SUCCESS) {  // Result is used!
        user->authenticated = 1;
        return SUCCESS;
    }

    user->authenticated = 0;
    return FAILURE;
}

// SAFE: Check error code
void process_file_safe(const char* path) {
    FILE* f = fopen(path, "r");
    if (f == NULL) {
        log_error("Failed to open file");
        return;
    }

    int error = validate_file(f);
    if (error != 0) {  // Error is checked!
        log_error("Validation failed");
        fclose(f);
        return;
    }

    read_contents(f);
    fclose(f);
}

// SAFE: Don't compute unused values
int calculate_safe(int a, int b) {
    int result = a + b;  // Only compute what's needed
    return result;
}

// SAFE: Correct variable scope
void search_safe(int* arr, int size, int target) {
    int found = 0;

    for (int i = 0; i < size; i++) {
        if (arr[i] == target) {
            found = 1;  // Same scope, no shadowing
            break;
        }
    }

    if (found) {
        printf("Found!\n");
    }
}

// SAFE: Prevent optimization of security-sensitive clearing
void process_secret_safe(const char* secret) {
    volatile char buffer[256];
    strncpy((char*)buffer, secret, sizeof(buffer));

    compute_hash((char*)buffer);

    // Use explicit_bzero or volatile to prevent optimization
    explicit_bzero((char*)buffer, sizeof(buffer));
}

// Or use platform-specific secure zero
#ifdef _WIN32
    SecureZeroMemory(buffer, sizeof(buffer));
#else
    explicit_bzero(buffer, sizeof(buffer));
#endif

// SAFE: Combine validation results
int validate_safe(Data* data) {
    int format_valid = check_format(data);
    int content_valid = check_content(data);

    // Both results used!
    return format_valid && content_valid;
}
// SAFE: C++ with proper value usage
class SafeProcessor {
public:
    std::pair<int, int> process(const std::vector<int>& data) {
        int total = 0;
        int count = 0;

        for (int val : data) {
            total += val;
            count++;
        }

        return {total, count};  // Both values used!
    }

    // Or remove unused variable
    int processTotalOnly(const std::vector<int>& data) {
        int total = 0;
        for (int val : data) {
            total += val;
        }
        return total;
    }

    std::string format(int value) {
        return "Value: " + std::to_string(value);  // No dead store
    }

    bool validate(const Request& req) {
        // Combine results properly
        return checkFormat(req) && checkContent(req);
    }
};

// SAFE: Properly use optional
void handle_safe(std::optional<int> value) {
    if (value) {
        int result = *value;
        process(result);  // result is used!
    }
}

// Or use value_or directly
void handle_safe_v2(std::optional<int> value) {
    process(value.value_or(0));  // No intermediate dead store
}

// SAFE: Use [[maybe_unused]] for intentionally unused
void debug_function([[maybe_unused]] int debug_value) {
#ifdef DEBUG
    log(debug_value);  // Only used in debug builds
#endif
}

// SAFE: Use std::ignore for intentionally ignored
void ignore_properly() {
    auto [used, ignored] = get_pair();
    std::ignore = ignored;  // Explicitly mark as intentionally unused
    process(used);
}
// SAFE: JavaScript with proper value usage
function processSafe(data) {
    let sum = 0;

    for (let item of data) {
        sum += item;
    }

    return sum;  // Only compute what's needed
}

// Or return both values
function processWithCount(data) {
    let sum = 0;
    let count = 0;

    for (let item of data) {
        sum += item;
        count++;
    }

    return { sum, count };  // Both values returned
}

// SAFE: Single assignment
function calculateSafe(a, b) {
    return a + b;
}

// SAFE: Combine validations
function validateSafe(input) {
    return checkFormat(input) && checkContent(input);
}

// SAFE: No shadowing
function searchSafe(arr, target) {
    let found = false;

    for (let item of arr) {
        if (item === target) {
            found = true;  // Updates outer variable
            break;
        }
    }

    return found;
}

// Or use built-in methods
function searchModern(arr, target) {
    return arr.includes(target);
}

// ESLint rules:
// - "no-unused-vars"
// - "no-useless-assignment"

Exploited in the Wild

Security Check Bypasses

Authentication and authorization systems have been bypassed when security check results were computed but not used.

Sensitive Data Exposure

Sensitive data computed but not properly cleared has been exposed when compiler optimizations removed "unused" clearing operations.

Logic Errors in Critical Systems

Critical systems have malfunctioned when computed values that should have influenced behavior were never used.


Tools to test/exploit

  • GCC/Clang — -Wunused-variable, -Wunused-but-set-variable.

  • Coverity — detects dead stores and unused values.

  • ESLint — no-unused-vars rule.

  • PVS-Studio — static analysis for dead code.


CVE Examples

  • Various CVEs where security check results were not used.

  • Authentication bypasses from unused validation results.

  • Information disclosure from data not properly cleared.


References

  1. MITRE. "CWE-563: Assignment to Variable without Use." https://cwe.mitre.org/data/definitions/563.html

  2. CERT C. "MSC13-C: Detect and remove unused values." https://wiki.sei.cmu.edu/confluence/display/c/