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
| Impact | Details |
|---|---|
| Other | Scope: Other Quality Degradation - Code may behave inconsistently across platforms, leading to difficult-to-reproduce bugs and security issues. |
| Other | Scope: 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
- MITRE Corporation. "CWE-474: Use of Function with Inconsistent Implementations." https://cwe.mitre.org/data/definitions/474.html
- CERT C Secure Coding Standard. "MSC14-C. Do not introduce unnecessary platform dependencies."