Use of Function with Inconsistent Implementations

Description

Use of Function with Inconsistent Implementations is a vulnerability where code employs a function that behaves differently across operating systems, compilers, or library versions. These implementation variations can create security vulnerabilities when code is ported to new platforms or compiled in unexpected environments. The inconsistencies may manifest as different parameter interpretation, varying security risks, platform-specific availability, or changed return code meanings.

Risk

Functions with inconsistent implementations create unpredictable security behaviors. Code that works securely on one platform may have vulnerabilities on another. Security-critical string functions may have different buffer handling between platforms. Cryptographic functions may use different defaults or algorithms. Time functions may have different precision or overflow behavior. The risk is amplified because these issues often only manifest in production environments different from development, making them difficult to detect during testing.

Solution

During architecture and design phases, identify and reject APIs exhibiting inconsistent behavior when such deviations increase risk levels. Use well-documented, standardized APIs with consistent behavior across target platforms. Create abstraction layers that normalize platform-specific behavior. Document all platform assumptions and test on all deployment targets. Use static analysis tools to detect usage of functions with known implementation inconsistencies. Consider using portable libraries that provide consistent implementations across platforms.

Common Consequences

ImpactDetails
OtherScope: Other

Quality Degradation - Code may behave inconsistently across platforms, leading to difficult-to-reproduce bugs and security issues.
OtherScope: Other

Varies by Context - Security implications depend on which function is used and how its behavior differs between implementations.

Example Code

Vulnerable Code

// Vulnerable: strcpy/strncpy have inconsistent null-termination behavior
#include <string.h>

void vulnerable_string_copy(char *dest, const char *src, size_t dest_size) {
    // Vulnerable: strncpy behavior varies:
    // - May or may not null-terminate
    // - May or may not fill remaining buffer with nulls
    // - Different behavior with overlapping buffers

    strncpy(dest, src, dest_size);

    // On some implementations, dest is NOT null-terminated if src >= dest_size
    // On others, it's always null-terminated
    // Code assuming one behavior breaks on other platforms

    printf("Copied: %s\n", dest);  // May read past buffer
}

// Vulnerable: signal() behavior varies significantly
#include <signal.h>

void vulnerable_signal_handler() {
    // Vulnerable: signal() behavior differs:
    // - On System V: signal handler reset to SIG_DFL after first invocation
    // - On BSD: signal handler remains installed
    // - On Linux: depends on configuration and flags

    signal(SIGINT, handle_interrupt);

    // Code expecting BSD semantics breaks on System V
    // SIGINT handler may only work once, then crash program
}

// Vulnerable: printf format specifiers vary
void vulnerable_format_output(long value) {
    // Vulnerable: %ld behavior on different systems
    // - Size of 'long' varies (32 or 64 bits)
    // - Some systems require %lld for 64-bit

    printf("Value: %ld\n", value);  // May truncate on some platforms

    // Vulnerable: printf return value handling
    // Some implementations return -1 for errors
    // Others may return partial counts or different values
}
<?php
// Vulnerable: PHP array_merge behavior changed between versions
function vulnerable_config_merge($default_config, $user_config) {
    // Vulnerable: array_merge behavior differs:
    // - PHP 5.x vs 7.x: different handling of integer keys
    // - Different versions handle null values differently

    $config = array_merge($default_config, $user_config);

    // In some versions, integer keys are renumbered
    // In others, they're preserved
    // Security-relevant configuration may be wrong
    return $config;
}

// Vulnerable: crypt() has platform-dependent behavior
function vulnerable_password_hash($password) {
    // Vulnerable: crypt() algorithm selection varies by platform
    // - Linux: may support bcrypt, SHA-256, SHA-512
    // - Other systems: may only support DES (weak)
    // - Salt format interpretation varies

    $hash = crypt($password, '$6$salt$');

    // On systems not supporting SHA-512, may fall back to DES
    // Password security significantly weaker than expected
    return $hash;
}
?>
# Vulnerable: OS-dependent path handling
import os

def vulnerable_path_handling(user_path):
    # Vulnerable: os.path.join behavior differs:
    # - Windows: handles drive letters specially
    # - Unix: treats everything as path components
    # - Trailing slashes handled differently

    base = "/var/data"
    full_path = os.path.join(base, user_path)

    # On Windows: "C:\\evil" in user_path ignores base entirely
    # On Unix: "../" traversal may work differently
    return full_path

# Vulnerable: tempfile behavior varies
import tempfile

def vulnerable_temp_file():
    # Vulnerable: tempfile behavior differs:
    # - Default directory varies by OS
    # - Permission handling differs
    # - Some platforms may use predictable names

    fd, path = tempfile.mkstemp()

    # On some systems, temp files may be world-readable
    # Path predictability varies by implementation
    return path
// Vulnerable: File path handling varies by OS
public class VulnerableFilePaths {

    public File getConfigFile(String filename) {
        // Vulnerable: Path separator varies
        // - Windows: backslash
        // - Unix: forward slash
        // File.separator helps but edge cases remain

        String path = "config/" + filename;  // Forward slash

        // On Windows, this may not resolve as expected
        // Mixed separators handled inconsistently
        return new File(path);
    }

    // Vulnerable: String.format locale-dependent
    public String formatNumber(double value) {
        // Vulnerable: Format behavior varies by locale
        // - Decimal separator: . vs ,
        // - Grouping separator: , vs . vs space
        // - Number of decimal places

        return String.format("%.2f", value);

        // "1234.56" in US locale
        // "1234,56" in German locale
        // May break parsing or security comparisons
    }
}

Fixed Code

// Fixed: Use consistent, well-defined string functions
#include <string.h>
#include <stdio.h>

void secure_string_copy(char *dest, const char *src, size_t dest_size) {
    if (dest_size == 0) return;

    // Fixed: Use strlcpy which has consistent behavior
    // Or implement explicitly to ensure consistency
    size_t src_len = strlen(src);
    size_t copy_len = (src_len < dest_size - 1) ? src_len : dest_size - 1;

    memcpy(dest, src, copy_len);
    dest[copy_len] = '\0';  // Fixed: Always null-terminate

    // Or use snprintf which has more consistent behavior
    snprintf(dest, dest_size, "%s", src);
}

// Fixed: Use sigaction instead of signal
#include <signal.h>

void secure_signal_handler() {
    // Fixed: sigaction has more consistent, portable behavior
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));

    sa.sa_handler = handle_interrupt;
    sa.sa_flags = SA_RESTART;  // Explicit flags for consistent behavior
    sigemptyset(&sa.sa_mask);

    sigaction(SIGINT, &sa, NULL);

    // Behavior is now consistent across platforms
}

// Fixed: Use explicit format specifiers
#include <inttypes.h>

void secure_format_output(int64_t value) {
    // Fixed: Use fixed-width types and their format macros
    printf("Value: %" PRId64 "\n", value);

    // Or use explicitly sized types
    int32_t val32 = (int32_t)value;
    printf("32-bit: %" PRId32 "\n", val32);
}
<?php
// Fixed: Explicit merge with defined behavior
function secure_config_merge($default_config, $user_config) {
    // Fixed: Implement explicit merge behavior
    $config = [];

    // Copy defaults
    foreach ($default_config as $key => $value) {
        $config[$key] = $value;
    }

    // Override with user config
    foreach ($user_config as $key => $value) {
        // Fixed: Explicit key handling
        if (is_string($key)) {
            $config[$key] = $value;
        }
        // Ignore integer keys from user config for security
    }

    return $config;
}

// Fixed: Use password_hash with explicit algorithm
function secure_password_hash($password) {
    // Fixed: Use password_hash with explicit, consistent algorithm
    $options = [
        'cost' => 12,  // Explicit cost factor
    ];

    // Fixed: PASSWORD_BCRYPT has consistent behavior across PHP versions
    $hash = password_hash($password, PASSWORD_BCRYPT, $options);

    // Or use PASSWORD_ARGON2ID on PHP 7.3+
    // $hash = password_hash($password, PASSWORD_ARGON2ID);

    return $hash;
}
?>
# Fixed: Platform-consistent path handling
import os
from pathlib import Path

def secure_path_handling(user_path):
    # Fixed: Use pathlib for consistent behavior
    base = Path("/var/data")

    # Fixed: Resolve and validate path
    try:
        user_part = Path(user_path)

        # Fixed: Reject absolute paths in user input
        if user_part.is_absolute():
            raise ValueError("Absolute paths not allowed")

        # Fixed: Resolve and check containment
        full_path = (base / user_part).resolve()

        if not str(full_path).startswith(str(base.resolve())):
            raise ValueError("Path traversal detected")

        return str(full_path)

    except (ValueError, OSError) as e:
        raise SecurityException(f"Invalid path: {e}")

# Fixed: Consistent temp file handling
import tempfile
import os

def secure_temp_file():
    # Fixed: Explicit directory and permissions
    temp_dir = os.environ.get('SECURE_TEMP', '/var/tmp/secure')
    os.makedirs(temp_dir, mode=0o700, exist_ok=True)

    # Fixed: Create with explicit permissions
    fd, path = tempfile.mkstemp(dir=temp_dir)

    # Fixed: Set restrictive permissions
    os.chmod(path, 0o600)

    return fd, path
// Fixed: Platform-independent file handling
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.NumberFormat;
import java.util.Locale;

public class SecureFilePaths {

    public Path getConfigFile(String filename) {
        // Fixed: Use Paths.get for platform-independent handling
        Path configDir = Paths.get("config");

        // Fixed: Validate filename
        if (filename.contains("..") ||
            filename.contains("/") ||
            filename.contains("\\")) {
            throw new SecurityException("Invalid filename");
        }

        return configDir.resolve(filename);
    }

    // Fixed: Explicit locale for number formatting
    public String formatNumber(double value) {
        // Fixed: Use explicit Locale for consistent behavior
        NumberFormat formatter = NumberFormat.getInstance(Locale.US);
        formatter.setMinimumFractionDigits(2);
        formatter.setMaximumFractionDigits(2);

        return formatter.format(value);
    }

    // Fixed: Binary format for machine-readable data
    public String formatMachineReadable(double value) {
        // Fixed: Use locale-independent format for data exchange
        return String.format(Locale.ROOT, "%.2f", value);
    }
}

CVE Examples

No specific CVEs are listed in the MITRE database for this CWE. However, the pattern is documented in:

  • Seven Pernicious Kingdoms taxonomy as "Inconsistent Implementations"
  • Cross-platform security advisories for libc function behavior differences

References

  1. MITRE Corporation. "CWE-474: Use of Function with Inconsistent Implementations." https://cwe.mitre.org/data/definitions/474.html
  2. CERT C Secure Coding Standard. "MSC14-C. Do not introduce unnecessary platform dependencies."