Back to Blog
medium SEVERITY5 min read

Why strtok() is Dangerous: A Critical Security Fix in libscram

A medium-severity vulnerability was recently patched in libscram's SCRAM authentication implementation, replacing the unsafe strtok() function with its thread-safe alternative strtok_r(). This seemingly small change prevents potential buffer corruption, race conditions, and authentication bypass vulnerabilities that could compromise application security in multi-threaded environments.

O
By Orbis AppSec
Published March 6, 2026Reviewed June 3, 2026

Answer Summary

The vulnerability is an unsafe use of strtok() in C's libscram SCRAM authentication implementation (CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization). In multi-threaded applications, strtok() maintains static internal state across calls, causing race conditions when multiple threads parse authentication credentials simultaneously. The fix replaces strtok() with strtok_r(), which uses caller-provided state buffers, eliminating shared state and ensuring thread-safe token parsing.

Vulnerability at a Glance

cweCWE-362 (Concurrent Execution using Shared Resource with Improper Synchronization)
fixReplace strtok() with strtok_r() which uses caller-provided state buffers
riskRace conditions, buffer corruption, authentication bypass in multi-threaded environments
languageC
root causestrtok() uses static internal state that is not thread-safe
vulnerabilityUnsafe strtok() use in multi-threaded SCRAM authentication

Introduction

String tokenization is one of the most common operations in C programming, but using the wrong function can introduce subtle yet serious security vulnerabilities. The libscram library, which implements the SCRAM (Salted Challenge Response Authentication Mechanism) protocol, recently fixed a vulnerability involving the use of the unsafe strtok() function in its authentication code.

This fix matters because SCRAM is widely used for authentication in databases like PostgreSQL and MongoDB, message queues, and other systems requiring secure password verification. A vulnerability in authentication code can have cascading effects on the entire security posture of an application.

The Vulnerability Explained

What Makes strtok() Dangerous?

The strtok() function has several characteristics that make it unsuitable for modern, security-conscious applications:

  1. Destructive Modification: strtok() permanently modifies the input buffer by replacing delimiter characters with null terminators (\0). This means your original string is destroyed during tokenization.

  2. Global State: strtok() uses internal static storage to remember where it left off between calls. This makes it inherently non-reentrant and non-thread-safe.

  3. Race Conditions: In multi-threaded applications, multiple threads calling strtok() simultaneously can corrupt each other's state, leading to unpredictable behavior.

Real-World Impact in Authentication Code

In the context of libscram's authentication implementation, these issues could manifest as:

Buffer Corruption: If the authentication string needs to be preserved for logging, retry logic, or validation, the destructive nature of strtok() could cause unexpected failures or expose sensitive data.

Thread Safety Issues: Modern applications often handle multiple authentication requests concurrently. Using strtok() in such scenarios could cause:
- Authentication tokens to be parsed incorrectly
- Credentials from one user to leak into another user's session
- Complete authentication bypass in race condition scenarios

Example Attack Scenario:

// Vulnerable code pattern
char auth_string[256];
strcpy(auth_string, user_provided_data);

// Thread 1: Parsing user Alice's credentials
char *token1 = strtok(auth_string, ",");

// Thread 2: Simultaneously parsing user Bob's credentials
// This corrupts Thread 1's state!
char *token2 = strtok(other_auth_string, ",");

// Thread 1 continues, but now gets corrupted data
char *next_token = strtok(NULL, ","); // May return Bob's data!

In this scenario, Alice could potentially authenticate with Bob's privileges, or authentication could fail completely, causing denial of service.

The Fix

What Changed?

The fix replaces strtok() with strtok_r(), the reentrant (thread-safe) version:

Before (Vulnerable Code):

char *token;
char buffer[256];

// First call with the string
token = strtok(buffer, ",");
while (token != NULL) {
    process_token(token);
    // Subsequent calls with NULL
    token = strtok(NULL, ",");
}

After (Secure Code):

char *token;
char *saveptr;  // Context pointer for strtok_r
char buffer[256];

// First call with the string and saveptr
token = strtok_r(buffer, ",", &saveptr);
while (token != NULL) {
    process_token(token);
    // Subsequent calls with NULL and same saveptr
    token = strtok_r(NULL, ",", &saveptr);
}

How Does This Solve the Problem?

The strtok_r() function addresses all the security concerns:

  1. Explicit State Management: The saveptr parameter stores the parsing context explicitly, rather than in global static storage.

  2. Thread Safety: Each thread can maintain its own saveptr, eliminating race conditions.

  3. Reentrancy: Functions using strtok_r() can be safely called recursively or from signal handlers.

  4. Same Functionality: The parsing behavior remains identical, but with security guarantees.

Security Improvement

This change provides:
- Elimination of race conditions in multi-threaded authentication scenarios
- Prevention of state corruption when multiple parsing operations occur simultaneously
- Improved code reliability and predictability
- Compliance with secure coding standards (CERT C, CWE-663)

Prevention & Best Practices

How to Avoid This Vulnerability

  1. Never use strtok() in new code: Always prefer strtok_r() or modern alternatives.

  2. Audit existing code: Search your codebase for strtok() usage:

grep -r "strtok(" --include="*.c" --include="*.cpp" .
  1. Use static analysis tools: Tools like:
    - Semgrep: Can detect strtok() usage automatically
    - Clang Static Analyzer: Identifies thread-safety issues
    - Coverity: Commercial tool with comprehensive C security checks
    - SonarQube: Open-source platform with C/C++ security rules

  2. Consider modern alternatives: For new projects, consider:
    - strsep(): BSD-style alternative (not POSIX standard)
    - Custom parsing: Using strchr() or strstr() for more control
    - C++ std::string: With modern tokenization methods

Security Recommendations

For C Developers:

// Good: Thread-safe tokenization
void parse_auth_data(char *data) {
    char *token, *saveptr;
    char *copy = strdup(data); // Work on a copy if original needed

    token = strtok_r(copy, ",", &saveptr);
    while (token != NULL) {
        // Process token safely
        token = strtok_r(NULL, ",", &saveptr);
    }

    free(copy);
}

Additional Security Measures:

  • Input validation: Always validate token format and length
  • Bounds checking: Use strnlen() and buffer size checks
  • Const correctness: Mark strings that shouldn't be modified as const
  • Code review: Flag any use of deprecated or unsafe functions

Relevant Security Standards

This vulnerability relates to:

  • CWE-663: Use of a Non-reentrant Function in a Concurrent Context
  • CWE-662: Improper Synchronization
  • CERT C Rule CON33-C: Avoid race conditions when using library functions
  • OWASP: A06:2021 – Vulnerable and Outdated Components

Conclusion

The replacement of strtok() with strtok_r() in libscram demonstrates that security isn't always about dramatic vulnerabilities—sometimes it's about eliminating subtle risks that could be exploited under the right conditions. In authentication code, where thread safety and data integrity are paramount, even "medium" severity issues deserve immediate attention.

Key Takeaways:

✅ Always use strtok_r() instead of strtok() in C code
✅ Thread safety matters, especially in authentication and security-critical code
✅ Static analysis tools can catch these issues before they reach production
✅ Regular security audits of dependencies help identify and fix vulnerabilities
✅ Small fixes can prevent large security incidents

If you're using libscram or any library that performs authentication, ensure you're running the latest patched version. For developers writing C code, make it a practice to audit your string manipulation functions—your future self (and your users) will thank you.

Update your dependencies, review your code, and stay secure!


Have you encountered similar issues with legacy C functions? Share your experiences in the comments below.

Frequently Asked Questions

What is unsafe strtok() use?

strtok() maintains a static internal pointer to track parsing position. In multi-threaded code, multiple threads calling strtok() simultaneously overwrite this shared state, causing race conditions and corrupted token parsing.

How do you prevent unsafe strtok() in C?

Always use strtok_r() (or strtok_s() on Windows) which takes an explicit state buffer parameter, eliminating shared state and making the function thread-safe.

What CWE is unsafe strtok() use?

CWE-362 (Concurrent Execution using Shared Resource with Improper Synchronization) and CWE-416 (Use After Free) in some contexts where freed memory is accessed due to race conditions.

Is using mutexes around strtok() enough to prevent this?

While mutexes would serialize access, they're unnecessary overhead. Using strtok_r() is the correct solution—it eliminates the race condition entirely rather than just serializing access to a flawed function.

Can static analysis detect unsafe strtok() use?

Yes, modern static analyzers like Semgrep, Clang Static Analyzer, and commercial tools can detect strtok() calls in multi-threaded contexts and flag them as potential race conditions.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #5348

Related Articles

critical

How buffer overflow happens in C tar header parsing and how to fix it

A critical buffer overflow vulnerability was discovered in `microtar/microtar.c` where the `raw_to_header()` and `header_to_raw()` functions used unbounded `strcpy()` and `sprintf()` calls to copy tar header fields. Malicious tar files with non-null-terminated name fields could overflow destination buffers, potentially leading to code execution. The fix replaces all unsafe string operations with bounded alternatives: `memcpy()` with explicit null-termination and `snprintf()` instead of `sprintf(

high

How buffer overflow from unsafe string copy functions happens in C network interface code and how to fix it

A high-severity buffer overflow vulnerability was discovered in `generic/eth-impl.c`, where unsafe `strncpy()` and `sprintf()` calls could write beyond buffer boundaries when handling network interface names and device filenames. The fix replaced these dangerous functions with bounded `snprintf()` calls that guarantee null-termination and prevent memory corruption.

critical

How buffer overflow happens in C sprintf() and how to fix it

A critical buffer overflow vulnerability was discovered in the ArrowTest() function in main/main.c, where sprintf() was writing formatted strings to a 24-byte buffer without bounds checking. By replacing sprintf() with snprintf() and specifying the buffer size, the vulnerability was eliminated, preventing attackers from corrupting heap memory through oversized width or height parameters.

critical

How buffer overflow in locale name processing happens in C and how to fix it

A critical buffer overflow vulnerability was discovered in `intl/localename.c` where the `gl_locale_name_canonicalize()` function used unsafe `strcpy()` operations to copy locale names into fixed-size buffers without bounds checking. An attacker controlling locale environment variables could overflow the destination buffer, leading to memory corruption and potential code execution. The fix replaced `strcpy()` with bounded `strncpy()` calls to prevent buffer overruns.

critical

How buffer overflow happens in C strcpy() and how to fix it

A critical buffer overflow vulnerability was discovered in `sbin/restore/tape.c` where the `setinput()` function used unsafe `strcpy()` to copy user-controlled input into a fixed-size buffer without bounds checking. The fix replaces `strcpy()` with `strlcpy()`, which enforces a maximum copy length and prevents the overflow. This vulnerability could have allowed attackers to corrupt memory and potentially execute arbitrary code through long command-line arguments.

high

How insecure string copy functions happen in C calculations.c and how to fix it

A high-severity buffer overflow vulnerability was discovered in `src/calculations.c` at line 37, where a two-step `strncpy` + manual null-termination pattern left the door open for subtle memory safety bugs when copying string data into the `entry->type` field. The fix replaces both lines with a single `snprintf` call that handles bounds and null-termination atomically, eliminating the risk entirely. This is a common C pitfall that affects production CLI tools and can be exploited when attacker-