Main Content

CERT C: Rule CON34-C

Declare objects shared between threads with appropriate storage durations

Since R2020a

Description

This checker is deactivated in a default Polyspace® as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).

Rule Definition

Declare objects shared between threads with appropriate storage durations.1

Polyspace Implementation

The rule checker checks for Automatic or thread local variable escaping from a C11 thread.

Examples

expand all

Issue

Automatic or thread local variable escaping from a C11 thread occurs when an automatic or thread local variable is passed by address from one C11 thread to another without ensuring that the variable stays alive through the duration of the latter thread.

Risk

An automatic or thread local variable is allocated on the stack at the beginning of a thread and its lifetime extends till the end of the thread. The variable is not guaranteed to be alive when a different thread accesses it.

For instance, consider the start function of a C11 thread with these lines:

int start_thread(thrd_t *tid) {
   int aVar = 0;
   if(thrd_success != thrd_create(tid, start_thread_child, &aVar) {
     //...
   }
}

The thrd_create function creates a child thread with start function start_thread_child and passes the address of the automatic variable aVarto this function. When this child thread accesses aVar, the parent thread might have completed execution and aVar is no longer on the stack. The access might result in reading unpredictable values.

Fix

When you pass a variable from one thread to another, make sure that the variable lifetime matches or exceeds the lifetime of both threads. You can achieve this synchronization in one of these ways:

  • Declare the variable static so that it does not go out of stack when the current thread completes execution.

  • Dynamically allocate the storage for the variable so that it is allocated on the heap instead of the stack and must be explicitly deallocated. Make sure that the deallocation happens after both threads complete execution.

These solutions require you to create a variable in nonlocal memory. Instead, you can use other solutions such as the shared keyword available with OpenMP's threading interface that allows you to safely share local variables across threads.

Example – Automatic or Thread-Local Variable Escaping Thread
#include <threads.h>
#include <stdio.h>

int create_child_thread(void *childVal) {
  int *res = (int *)childVal;
  printf("Result: %d\n", *res);
  return 0;
}

void create_parent_thread(thrd_t *tid, int *parentPtr) {
   if (thrd_success != thrd_create(tid, create_child_thread, parentPtr)) { //Noncompliant
    /* Handle error */
  }
}

int main(void) {
  thrd_t tid;
  int parentVal = 1;
  
  create_parent_thread(&tid, &parentVal);


  if (thrd_success != thrd_join(tid, NULL)) {
    /* Handle error */
  }
  return 0;
}

In this example, the value parentVal is local to the parent thread that starts in main and continues into the function create_parent_thread. However, in the body of create_parent_thread, the address of this local variable is passed to a child thread (the thread with start routine create_child_thread). The parent thread might have completed execution and the variable parentVal might have gone out of scope when the child thread accesses this variable.

The same issue appears if the variable is declared as thread-local, for instance with the C11 keyword _Thread_local (or thread_local):

_Thread_local int parentVal = 1;

Correction – Use Static Variables

One possible correction is to declare the variable parentVal as static so that the variable is on the stack for the entire duration of the program.

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

int create_child_thread(void *childVal) {
  int *res = (int *)childVal;
  printf("Result: %d\n", *res);
  return 0;
}

void create_parent_thread(thrd_t *tid, int *parentPtr) {
   if (thrd_success != thrd_create(tid, create_child_thread, parentPtr)) {
    /* Handle error */
  }
}

int main(void) {
  thrd_t tid;
  static int parentVal = 1;
  
  create_parent_thread(&tid, &parentVal);


  if (thrd_success != thrd_join(tid, NULL)) {
    /* Handle error */
  }
  return 0;
}
Correction – Use Dynamic Memory Allocation

One possible correction is to dynamically allocate storage for variables to be shared across threads and explicitly free the storage after the variable is no longer required.

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

int create_child_thread(void *childVal) {
  int *res = (int *)childVal;
  printf("Result: %d\n", *res);
  return 0;
}

void create_parent_thread(thrd_t *tid, int *parentPtr) {
   if (thrd_success != thrd_create(tid, create_child_thread, parentPtr)) {
    /* Handle error */
  }
}

int main(void) {
  thrd_t tid;
  int parentVal = 1;
  int parentPtr = (int*) malloc(sizeof(int));
  
  if(!parentPtr) {
      create_parent_thread(&tid, parentPtr);
     

      if (thrd_success != thrd_join(tid, NULL)) {
        /* Handle error */
      }
      free(parentPtr);
  }
  return 0;
}

Check Information

Group: Rule 14. Concurrency (CON)

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.