Call to Non-ubiquitous API

Description

Call to Non-ubiquitous API is a weakness where a product uses an API function that does not exist on all versions of the target platform or operating system. This includes functions that were added in newer versions, deprecated or removed in certain versions, or only available on specific platform variants. When code relies on such APIs without proper version checking or fallback mechanisms, the application may fail to run or behave incorrectly on systems where the API is unavailable. This is particularly problematic for security-related functions that may be missing on older systems.

Risk

Using non-ubiquitous APIs creates significant deployment and security risks. Applications may crash or fail to start on platforms lacking the required functions. Security features that depend on newer APIs may silently fail on older systems, leaving users unprotected. Functions deprecated for security reasons may still be used, exposing systems to known vulnerabilities. The application's behavior becomes inconsistent across different platform versions, complicating testing and support. Attackers may deliberately target older systems where security APIs are missing. Runtime errors from missing functions provide poor user experience and potential denial of service.

Solution

Identify the minimum platform version your application needs to support and document it clearly. Use compile-time checks to verify API availability when possible. Implement runtime version checking before calling APIs that may not exist. Provide fallback implementations for critical functionality when primary APIs are unavailable. Test on both the oldest and newest supported platform versions. Use platform abstraction layers that handle version differences internally. Avoid deprecated functions and plan migration paths when APIs are scheduled for removal.

Common Consequences

ImpactDetails
OtherScope: Other

Quality Degradation - Application fails or behaves inconsistently on platforms lacking the required API functions.
AvailabilityScope: Availability

DoS: Crash, Exit, or Restart - Calling non-existent functions causes runtime errors and application crashes.

Example Code

Vulnerable Code

// Vulnerable: Using Windows Vista+ API without version check
#include <windows.h>
#include <bcrypt.h>

// Vulnerable: BCrypt functions only available on Vista+
int vulnerable_generate_random(unsigned char* buffer, size_t length) {
    NTSTATUS status;

    // BCryptGenRandom doesn't exist on Windows XP
    status = BCryptGenRandom(NULL, buffer, length,
                             BCRYPT_USE_SYSTEM_PREFERRED_RNG);

    return NT_SUCCESS(status) ? 0 : -1;
    // Will crash on Windows XP!
}

// Vulnerable: Using POSIX.1-2008 function without check
#define _GNU_SOURCE
#include <string.h>

// Vulnerable: strnlen not available on all systems
size_t vulnerable_safe_strlen(const char* str, size_t maxlen) {
    // strnlen is POSIX.1-2008, not available on older systems
    return strnlen(str, maxlen);
    // May cause link error or runtime failure on old platforms
}

// Vulnerable: Using Linux-specific syscall
#include <sys/random.h>

int vulnerable_get_random(void* buf, size_t buflen) {
    // getrandom() added in Linux 3.17, glibc 2.25
    return getrandom(buf, buflen, 0);
    // Fails on older kernels/glibc
}
// Vulnerable: Using Java version-specific API
public class VulnerableJavaApi {

    // Vulnerable: String.isBlank() only in Java 11+
    public boolean vulnerable_isEmpty(String s) {
        return s.isBlank();  // NoSuchMethodError on Java 8!
    }

    // Vulnerable: Files.readString() only in Java 11+
    public String vulnerable_readFile(Path path) throws IOException {
        return Files.readString(path);  // Doesn't exist in Java 8
    }

    // Vulnerable: Using preview features
    public void vulnerable_patternMatch(Object obj) {
        // Pattern matching only in Java 14+ (preview) / 16+ (final)
        if (obj instanceof String s) {
            System.out.println(s.length());
        }
    }
}

// Vulnerable: Using Android API without version check
public class VulnerableAndroidApi {

    // Vulnerable: API level not checked
    public void vulnerable_createNotificationChannel(Context context) {
        // NotificationChannel added in API 26 (Android 8.0)
        NotificationChannel channel = new NotificationChannel(
            "my_channel", "My Channel", NotificationManager.IMPORTANCE_DEFAULT);

        NotificationManager nm = context.getSystemService(NotificationManager.class);
        nm.createNotificationChannel(channel);
        // Crashes on Android 7.1 and below!
    }
}
# Vulnerable: Using Python version-specific features
import sys

# Vulnerable: f-strings only in Python 3.6+
def vulnerable_format(name):
    return f"Hello, {name}!"  # SyntaxError on Python 3.5

# Vulnerable: := operator only in Python 3.8+
def vulnerable_walrus():
    if (n := len(some_list)) > 10:  # SyntaxError on Python 3.7
        print(f"List too long: {n}")

# Vulnerable: Using typing features from newer versions
from typing import TypedDict  # Only Python 3.8+

class Person(TypedDict):  # Fails on 3.7
    name: str
    age: int

Fixed Code

// Fixed: Runtime version check for Windows APIs
#include <windows.h>
#include <wincrypt.h>

// Function pointer for dynamic loading
typedef NTSTATUS (WINAPI *PFN_BCryptGenRandom)(
    BCRYPT_ALG_HANDLE, PUCHAR, ULONG, ULONG);

int secure_generate_random(unsigned char* buffer, size_t length) {
    // Try modern API first
    HMODULE bcrypt = LoadLibraryA("bcrypt.dll");
    if (bcrypt) {
        PFN_BCryptGenRandom pfnBCryptGenRandom =
            (PFN_BCryptGenRandom)GetProcAddress(bcrypt, "BCryptGenRandom");

        if (pfnBCryptGenRandom) {
            NTSTATUS status = pfnBCryptGenRandom(
                NULL, buffer, (ULONG)length,
                BCRYPT_USE_SYSTEM_PREFERRED_RNG);
            FreeLibrary(bcrypt);
            return NT_SUCCESS(status) ? 0 : -1;
        }
        FreeLibrary(bcrypt);
    }

    // Fallback to CryptoAPI (Windows XP compatible)
    HCRYPTPROV hProv;
    if (!CryptAcquireContext(&hProv, NULL, NULL,
                             PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
        return -1;
    }

    int result = CryptGenRandom(hProv, (DWORD)length, buffer) ? 0 : -1;
    CryptReleaseContext(hProv, 0);
    return result;
}

// Fixed: Compile-time and runtime checks for POSIX functions
#include <string.h>

size_t secure_safe_strlen(const char* str, size_t maxlen) {
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L
    // strnlen available
    return strnlen(str, maxlen);
#else
    // Fallback implementation
    size_t len = 0;
    while (len < maxlen && str[len] != '\0') {
        len++;
    }
    return len;
#endif
}

// Fixed: Linux version check with fallback
#include <unistd.h>
#include <sys/syscall.h>
#include <fcntl.h>

int secure_get_random(void* buf, size_t buflen) {
#if defined(__linux__)
    // Check for getrandom at runtime
    #ifdef SYS_getrandom
    long ret = syscall(SYS_getrandom, buf, buflen, 0);
    if (ret >= 0) {
        return 0;
    }
    if (errno != ENOSYS) {
        return -1;  // Real error, not missing syscall
    }
    #endif

    // Fallback to /dev/urandom
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd < 0) return -1;

    ssize_t result = read(fd, buf, buflen);
    close(fd);
    return (result == (ssize_t)buflen) ? 0 : -1;
#else
    // Non-Linux: use platform-specific method
    return platform_get_random(buf, buflen);
#endif
}
// Fixed: Java version-aware code
public class SecureJavaApi {

    // Fixed: Version check with fallback
    public boolean secure_isEmpty(String s) {
        if (s == null) return true;

        // Check Java version at runtime
        int version = getJavaVersion();
        if (version >= 11) {
            // Use reflection to call isBlank() if available
            try {
                return (Boolean) String.class
                    .getMethod("isBlank")
                    .invoke(s);
            } catch (Exception e) {
                // Fall through to fallback
            }
        }

        // Fallback for Java 8
        return s.trim().isEmpty();
    }

    private int getJavaVersion() {
        String version = System.getProperty("java.version");
        if (version.startsWith("1.")) {
            return Integer.parseInt(version.substring(2, 3));
        }
        int dot = version.indexOf('.');
        if (dot != -1) {
            return Integer.parseInt(version.substring(0, dot));
        }
        return Integer.parseInt(version);
    }

    // Fixed: Use compatible API
    public String secure_readFile(Path path) throws IOException {
        // Works on Java 8+
        byte[] bytes = Files.readAllBytes(path);
        return new String(bytes, StandardCharsets.UTF_8);
    }
}

// Fixed: Android API with version check
public class SecureAndroidApi {

    public void secure_createNotificationChannel(Context context) {
        // Fixed: Check API level before using new APIs
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                "my_channel", "My Channel",
                NotificationManager.IMPORTANCE_DEFAULT);

            NotificationManager nm = context.getSystemService(
                NotificationManager.class);
            nm.createNotificationChannel(channel);
        }
        // For older versions, channels aren't needed
    }

    // Using AndroidX for backward compatibility
    public void secure_showNotification(Context context) {
        NotificationCompat.Builder builder =
            new NotificationCompat.Builder(context, "my_channel")
                .setSmallIcon(R.drawable.notification_icon)
                .setContentTitle("Title")
                .setContentText("Message")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT);

        // NotificationCompat handles version differences
        NotificationManagerCompat nm = NotificationManagerCompat.from(context);
        nm.notify(1, builder.build());
    }
}
# Fixed: Python version checks
import sys

# Fixed: Version check for f-strings
def secure_format(name):
    if sys.version_info >= (3, 6):
        return f"Hello, {name}!"
    else:
        return "Hello, {}!".format(name)

# Fixed: Conditional imports with fallbacks
try:
    from typing import TypedDict
except ImportError:
    # Python < 3.8 fallback
    from typing import Dict
    TypedDict = Dict  # Simplified fallback

# Fixed: Feature detection
def secure_get_list_length():
    some_list = [1, 2, 3, 4, 5]

    # Walrus operator only in 3.8+
    if sys.version_info >= (3, 8):
        # Can't use walrus here as it would be syntax error on older versions
        # Use exec() or separate module for version-specific code
        pass

    # Compatible approach
    n = len(some_list)
    if n > 10:
        print("List too long: {}".format(n))

# Fixed: Using compatibility libraries
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from typing import TypedDict

# Runtime compatibility
try:
    from typing import TypedDict
except ImportError:
    class TypedDict(dict):
        """Fallback TypedDict for Python < 3.8"""
        def __init_subclass__(cls, **kwargs):
            pass

CVE Examples

No specific CVEs are commonly attributed to this CWE directly, though many vulnerabilities arise from missing security functions on older platforms.


References

  1. MITRE Corporation. "CWE-589: Call to Non-ubiquitous API." https://cwe.mitre.org/data/definitions/589.html
  2. Microsoft. "Version Helper Functions."
  3. Android Developers. "Build.VERSION_CODES."