Main Content

Function that can spuriously wake up not wrapped in loop

Loop checks wake-up condition after possible spurious wake-up

Description

This defect occurs when the following wait-on-condition functions are called from outside a loop:

  • C functions:

    • cnd_wait()

    • cnd_timedwait()

  • POSIX functions:

    • pthread_cond_wait()

    • pthread_cond_timedwait()

  • C++ std::condition_variable and std::condition_variable_any class member functions:

    • wait()

    • wait_until()

    • wait_for()

Wait-on-condition functions pause the execution of the calling thread when a specified condition is met. The thread wakes up and resumes once another thread notifies it with cnd_broadcast() or an equivalent function. The wake-up notification can be spurious or malicious.

Risk

If a thread receives a spurious wake-up notification and the condition of the wait-on-condition function is not checked, the thread can wake up prematurely. The wake-up can cause unexpected control flow, indefinite blocking of other threads, or denial of service.

Fix

Wrap wait-on-condition functions that can wake up spuriously in a loop. The loop checks the wake-up condition after a possible spurious wake-up notification.

Examples

expand all

#include <stdio.h>
#include <stddef.h>
#include <threads.h>

#define THRESHOLD 100

static mtx_t lock;
static cnd_t cond;

void func(int input)
{
    if (thrd_success != mtx_lock(&lock)) {
        /* Handle error */
    }
    /* test condition to pause thread */
    if (input > THRESHOLD) {
        if (thrd_success != cnd_wait(&cond, &lock)) {
            /* Handle error */
        }
    }
    /* Proceed if condition to pause does not hold */


    if (thrd_success != mtx_unlock(&lock)) {
        /* Handle error */
    }
}

In this example, the thread uses cnd_wait() to pause execution when input is greater than THRESHOLD. The paused thread can resume if another thread uses cnd_broadcast(), which notifies all the threads. This notification causes the thread to wake up even if the pause condition is still true.

Correction — Wrap cnd_wait() in a while Loop

One possible correction is to wrap cnd_wait() in a while loop. The loop checks the pause condition after the thread receives a possible spurious wake-up notification.

#include <stdio.h>
#include <stddef.h>
#include <threads.h>

#define THRESHOLD 100

static mtx_t lock;
static cnd_t cond;

void func(int input)
{
    if (thrd_success != mtx_lock(&lock)) {
        /* Handle error */
    }
    /* test condition to pause thread */
    while (input > THRESHOLD) {
        if (thrd_success != cnd_wait(&cond, &lock)) {
            /* Handle error */
        }
    }
    /* Proceed if condition to pause does not hold */


    if (thrd_success != mtx_unlock(&lock)) {
        /* Handle error */
    }
}
 

Result Information

Group: Concurrency
Language: C | C++
Default: Off
Command-Line Syntax: SPURIOUS_WAKEUP_NOT_WRAPPED_IN_LOOP
Impact: Low

Version History

Introduced in R2018b