Main Content

CERT C: Rule POS47-C

Do not use threads that can be canceled asynchronously

Since R2020a

Description

Rule Definition

Do not use threads that can be canceled asynchronously.1

Polyspace Implementation

The rule checker checks for Asynchronously cancellable thread.

Examples

expand all

Issue

This issue occurs when you use pthread_setcanceltype with argument PTHREAD_CANCEL_ASYNCHRONOUS to set the cancellability type of a calling thread to asynchronous (or immediate). An asynchronously cancellable thread can be canceled at any time, usually immediately upon receiving a cancellation request.

Risk

The calling thread might be canceled in an unsafe state that could result in a resources leak, a deadlock, a data race, data corruption, or unpredictable behavior.

Fix

Remove the call to pthread_setcanceltype with argument PTHREAD_CANCEL_ASYNCHRONOUS to use the default cancellability type PTHREAD_CANCEL_DEFERRED instead. With the default cancellability type, the thread defers cancellation requests until it calls a function that is a cancellation point.

Example - Cancellability Type of Thread Set to Asynchronous
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

static int fatal_error(void)
{
    exit(1);
}


volatile int a = 5;
volatile int b = 10;

pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;

void* swap_values_thread(void* dummy)
{
    int i;
    int c;
    int result;
    if ((result =
             pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &i)) != 0) { //Noncompliant
        /* handle error */
        fatal_error();
    }
    while (1) {
        if ((result = pthread_mutex_lock(&global_lock)) != 0) {
            /* handle error */
            fatal_error();
        }
        c = b;
        b = a;
        a = c;
        if ((result = pthread_mutex_unlock(&global_lock)) != 0) {
            /* handle error */
            fatal_error();
        }
    }
    return NULL;
}

int main(void)
{
    int result;
    pthread_t worker;

    if ((result = pthread_create(&worker, NULL, swap_values_thread, NULL)) != 0) {
        /* handle error */
        fatal_error();
    }

    /* Additional code */

    if ((result = pthread_cancel(worker)) != 0) {
        /* handle error */
        fatal_error();
    }


    if ((result = pthread_join(worker, 0)) != 0) {
        /* handle error */
        fatal_error();
    }

    if ((result = pthread_mutex_lock(&global_lock)) != 0) {
        /* handle error */
        fatal_error();
    }
    printf("a: %i | b: %i", a, b);
    if ((result = pthread_mutex_unlock(&global_lock)) != 0) {
        /* handle error */
        fatal_error();
    }

    return 0;
}

In this example, the cancellability type of the worker thread is set to asynchronous. The mutex global_lock helps ensure that the worker and main threads do not access variables a and b at the same time. However, the worker thread might be canceled while holding global_lock, and the main thread will never acquire global_lock, which results in a deadlock.

Correction — Use the Default Cancellability Type

One possible correction is to remove the call to pthread_setcanceltype. By default, the cancellability type of a new thread is set to PTHREAD_CANCEL_DEFERRED. The worker thread defers cancellation requests until it calls a function that is a cancellation point.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

static int fatal_error(void)
{
    exit(1);
}


volatile int a = 5;
volatile int b = 10;

pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;

void* swap_values_thread(void* dummy)
{
    int i;
    int c;
    int result;
    while (1) {
        if ((result = pthread_mutex_lock(&global_lock)) != 0) {
            /* handle error */
            fatal_error();
        }
        c = b;
        b = a;
        a = c;
        if ((result = pthread_mutex_unlock(&global_lock)) != 0) {
            /* handle error */
            fatal_error();
        }
    }
    return NULL;
}

int main(void)
{
    int result;
    pthread_t worker;

    if ((result = pthread_create(&worker, NULL, swap_values_thread, NULL)) != 0) {
        /* handle error */
        fatal_error();
    }

    /* Additional code */

    if ((result = pthread_cancel(worker)) != 0) {
        /* handle error */
        fatal_error();
    }


    if ((result = pthread_join(worker, 0)) != 0) {
        /* handle error */
        fatal_error();
    }

    if ((result = pthread_mutex_lock(&global_lock)) != 0) {
        /* handle error */
        fatal_error();
    }
    printf("a: %i | b: %i", a, b);
    if ((result = pthread_mutex_unlock(&global_lock)) != 0) {
        /* handle error */
        fatal_error();
    }

    return 0;
}

Check Information

Group: Rule 50. POSIX (POS)

Version History

Introduced in R2020a


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.