CertC-CON05

Do not perform operations that can block while holding a lock

Required inputs: IR

If a lock is being held and an operation that can block is performed, any other thread that needs to acquire that lock may also block. This condition can degrade system performance or cause a deadlock to occur.  Blocking calls include, but are not limited to, network, file, and console I/O.

Using a blocking operation while holding a lock may be unavoidable for a portable solution. For instance, file access could be protected via a lock to prevent multiple threads from mutating the contents of the file. Or, a thread may be required to block while holding one or more locks and waiting to acquire another lock. In these cases, attempt to hold the lock for the least time required. Additionally, if acquiring multiple locks, the order of locking must avoid deadlock, as specified in CON35-C. Avoid deadlock by locking in a predefined order.

Noncompliant Code Example

This noncompliant example calls fopen() while a mutex is locked. The calls to fopen() and fclose() are blocking and may block for an extended period of time if the file resides on a network drive. While the call is blocked, other threads that are waiting for the lock are also blocked.

#include <stdio.h>
#include <threads.h>
 
mtx_t mutex;

int thread_foo(void *ptr) {
  int result;
  FILE *fp;

  if ((result = mtx_lock(&mutex)) != thrd_success) {
    /* Handle error */
  }
 
  fp = fopen("SomeNetworkFile", "r");
  if (fp != NULL) {
    /* Work with the file */
    fclose(fp);
  }
 
  if ((result = mtx_unlock(&mutex)) != thrd_success) {
    /* Handle error */
  }

  return 0;
}

int main(void) {
  thrd_t thread;
  int result;

  if ((result = mtx_init(&mutex, mtx_plain)) != thrd_success) {
    /* Handle error */
  }

  if (thrd_create(&thread, thread_foo, NULL) != thrd_success) {
    /* Handle error */
  }

  /* ... */

  if (thrd_join(thread, NULL) != thrd_success) {
    /* Handle error */
  }

  mtx_destroy(&mutex);

  return 0;
}
Compliant Solution (Block while Not Locked)

This compliant solution performs the file operations when the lock has not been acquired. The blocking behavior consequently affects only the thread that called the blocking function.

#include <stdio.h>
#include <threads.h>
 
mtx_t mutex;
 
int thread_foo(void *ptr) {
  int result;
  FILE *fp = fopen("SomeNetworkFile", "r");
 
  if (fp != NULL) {
    /* Work with the file */
    fclose(fp);
  }

  if ((result = mtx_lock(&mutex)) != thrd_success) {
    /* Handle error */
  }

  /* ... */

  if ((result = pthread_mutex_unlock(&mutex)) != 0) {
    /* Handle error */
  }

  return 0;
}
Risk Assessment

Blocking or lengthy operations performed within synchronized regions could result in a deadlocked or an unresponsive system.

Recommendation Severity Likelihood Remediation Cost Priority Level
CON05-C Low Probable High P2 L3
Related Guidelines
Taxonomy Taxonomy item Relationship
CERT Oracle Secure Coding Standard for Java LCK09-J. Do not perform operations that can block while holding a lock Prior to 2018-01-12: CERT: Unspecified Relationship
MITRE CWE CWE-557 Prior to 2018-01-12:
MITRE CWE CWE-662 Prior to 2018-01-12:
Excerpt from SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition) and SEI CERT C Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/recommendations/concurrency-con/con05-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

blocking_call_in_critical_section

Call to possible blocking function ‘{}’ in critical section.

None

False

Options

blocking_function_calls

blocking_function_calls : set[bauhaus.analysis.config.QualifiedName] = {'fopen'}

Warn if a call to a function with name in this set, is inside a critical section. E.g. a function that waits for I/O or is very slow.
 

enter_critical_functions

enter_critical_functions : set[bauhaus.analysis.config.QualifiedName] = {'mtx_lock'}

Set of function names to enter a critical region.
 

enter_critical_macros

enter_critical_macros : set[bauhaus.analysis.config.MacroName] = set()

Set of macro names to enter a critical region (macros must expand to asm() statement).
 

exit_critical_functions

exit_critical_functions : set[bauhaus.analysis.config.QualifiedName] = {'mtx_unlock'}

Set of function names to exit a critical region.
 

exit_critical_macros

exit_critical_macros : set[bauhaus.analysis.config.MacroName] = set()

Set of macro names to exit a critical region (macros must expand to asm() statement).
 

nested_critical_regions

nested_critical_regions : bool = True

If set to true, critical regions nest; if set to false, a single exit-critical-region terminates all open critical regions.