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
•Technically reviewed by Anupam Mediratta•Published March 6, 2026•Reviewed 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)

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.

Prevention and further reading

Related Articles

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 in stb_image.h memcpy happens in C image parsing and how to fix it

A critical buffer overflow vulnerability was discovered in stb_image.h at line 4823, where a memcpy operation copied image data without validating buffer bounds. The multiplication of width (x) and channel count (img_n) could overflow or exceed allocated memory, allowing attackers to corrupt memory through malicious PNG files. The fix adds an explicit size_t cast to prevent integer overflow during the buffer size calculation.

critical

How buffer overflow happens in C gdb-server and how to fix it

A critical buffer overflow vulnerability was discovered in `src/st-util/gdb-server.c` where unbounded `memcpy()` and `strcpy()` calls could write beyond allocated buffer boundaries when processing user-supplied command-line arguments. The fix replaces all unsafe string operations with bounds-checked alternatives like `snprintf()` and `memcpy()` with explicit length validation.

high

How buffer overflow via strcpy() happens in C ubus.c and how to fix it

A high-severity buffer overflow vulnerability was discovered and fixed in `ubus.c` at line 577, where `strcpy()` was used to copy user-provided strings into dynamically allocated buffers without explicit size bounds checking. While current allocation logic correctly sizes the buffer, the use of `strcpy()` creates a dangerous coding pattern that could lead to exploitable memory corruption if the allocation logic ever changes or a TOCTOU race condition is introduced. The fix replaces the unbounded

high

How buffer overflow via strcpy() happens in Nordic BLE C firmware and how to fix it

A high-severity buffer overflow vulnerability was discovered in the Nordic BLE Central Demo firmware, where unsafe `strcpy()` and `sprintf()` calls in the `BleDevDiscovered()` function could allow attackers to overflow stack buffers by sending specially crafted BLE service discovery responses. The fix replaced all unbounded string operations with size-checked `snprintf()` calls, preventing potential remote code execution in embedded Bluetooth devices.

critical

How heap buffer overflow happens in C polygon rendering and how to fix it

A critical heap buffer overflow vulnerability was discovered in `sgl_polygon.c` where the `memcpy()` function copied user-controlled vertex data without validating that the count parameter didn't exceed the allocated buffer capacity. This could allow attackers to overwrite adjacent heap memory, potentially corrupting function pointers or heap metadata. The fix adds a bounds check before the copy operation to ensure the count never exceeds the maximum allocated size.