...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdatomic.h> /* Sets index to point to index of maximum element in array and value to contain maximum array value. */ void find_max_element(_Atomic int array[], size_t index, int value); /* ... */ void func(void) { _Atomic int array[]; size_t index; int value; find_max_element( array, &index, &value); /* ... */ if (!atomic_compare_exchange_weak( array[index], value, 0)) { /* Handle error */ } } |
The compare-and-swap operation will set array[index] to 0 if and only if it is currently set to value. However, this does not necessarily zero out the maximum value of the array because:
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdatomic.h> #include <threads.h> void func(void) { _Atomic int array[]; size_t index; int value; mtx_t array_mutex; int result; if ((result = mtx_lock(&array_mutex)) == thrd_error) { /* Handle error */ } find_max_element( array, &index, &value); /* ... */ if (!atomic_compare_exchange_weak( array[index], value, 0)) { /* Handle error */ } if ((result = mtx_unlock(&array_mutex)) == thrd_error) { /* Handle error */ } } |
Noncompliant Code Example (GNU Glib)
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <glib.h>
#include <glib-object.h>
typedef struct node_s {
void *data;
Node *next;
} Node;
typedef struct queue_s {
Node *head;
Node *tail;
} Queue;
Queue* queue_new(void) {
Queue *q = g_slice_new( sizeof(Queue));
q->head = q->tail = g_slice_new0new( sizeof(Node));
return q;
}
void queue_enqueue(Queue *q, gpointer data) {
Node *node;
Node *tail;
Node *next;
node = g_slice_new(Node);
node->data = data;
node->next = NULL;
while (TRUE) {
tail = q->tail;
next = tail->next;
if (tail != q->tail) {
continue;
}
if (next != NULL) {
CAS(&q->tail, tail, next);
continue;
}
if (CAS( &tail->next, null, node)) {
break;
}
}
CAS(&q->tail, tail, node);
}
gpointer queue_dequeue(Queue *q) {
Node *node;
Node *tail;
Node *next;
while (TRUE) {
head = q->head;
tail = q->tail;
next = head->next;
if (head != q->head) {
continue;
}
if (next == NULL) {
return NULL; //* Empty */
}
if (head == tail) {
CAS(&q->tail, tail, next);
continue;
}
data = next->data;
if (CAS(&q->head, head, next)) {
break;
}
}
g_slice_free(Node, head);
return data;
}
|
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
// Hazard pointers types and structure structure HPRecType { HP[K]:*Nodetype; Next:*HPRecType; } // The header of the HPRec list HeadHPRec: *HPRecType; // Per-thread private variables rlist: listType; // initially empty rcount: integer; // initially 0 // The retired node routine RetiredNode(node:*NodeType) { rlist.push(node); rcount++; if(rcount >= R) Scan(HeadHPRec); } // The scan routine Scan(head:*HPRecType) { // Stage1: Scan HP list and insert non-null values in plist plist.init(); hprec<-head; while (hprec != null) { for (i<-0 to K-1) { hptr<-hprec^HP[i]; if (hptr!= null) plist.insert(hptr); } hprec<-hprec^Next; } // Stage 2: search plist tmplist<-rlist.popAll(); rcount<-0; node<-tmplist.pop(); while (node != null) { if (plist.lookup(node)) { rlist.push(node); rcount++; } else { PrepareForReuse(node); } node<-tmplist.pop(); } plist.free(); } |
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <glib.h> #include <glib-object.h> void queue_enqueue(Queue *q, gpointer data) { Node *node; Node *tail; Node *next; node = g_slice_new(Node); node->data = data; node->next = NULL; while (TRUE) { tail = q->tail; HAZARD_SET(0, tail); HAZARD_SET(0, tail); //* Mark tail as hazardous */ if (tail != q->tail) { ///* Check tail hasn't changed */ continue; } next = tail->next; if (tail != q->tail) { continue; } if (next != NULL) { CAS(&q->tail, tail, next); continue; } if (CAS(&tail->next, null, node) { break; } } CAS(&q->tail, tail, node); } gpointer queue_dequeue(Queue *q) { Node *node; Node *tail; Node *next; while (TRUE) { head = q->head; LF_HAZARD_SET(0, head); //* Mark head as hazardous. */ if (head != q->head) { //* Check head hasn't changed. */ continue; } tail = q->tail; next = head->next; LF_HAZARD_SET(1, next); //* Mark next as hazardous. */ if (head != q->head) { continue; } if (next == NULL) { return NULL; //* Empty */ } if (head == tail) { CAS(&q->tail, tail, next); continue; } data = next->data; if (CAS(&q->head, head, next)) { break; } } LF_HAZARD_UNSET(head); ); //* Retire head, and perform // reclamation if needed. */ return data; } |
Compliant Solution (GNU Glib, Mutex)
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <glib-object.h>
#include <threads.h>
typedef struct node_s {
void *data;
Node *next;
} Node;
typedef struct queue_s {
Node *head;
Node *tail;
mtx_t mutex;
} Queue;
Queue* queue_new(void) {
Queue *q = g_slice_new( sizeof(Queue));
q->head = q->tail = g_slice_new0new( sizeof(Node));
return q;
}
int queue_enqueue(Queue *q, gpointer data) {
Node *node;
Node *tail;
Node *next;
/* Lock the queue before accessing the contents and
check the return code for success. */
if(mtx_lock(&(q->mutex)) != thrd_success) {
return -1; /* Indicate failure */
}
else {
node = g_slice_new(Node);
node->data = data;
node->next = NULL;
if(q->head == NULL) {
q->head = node;
q->tail = node;
}
else {
q->tail->next = node;
q->tail = node;
}
/* Unlock the mutex and chech the return code. */
if (mtx_unlock(&(queue->mutex)) != thrd_success) {
return -1; /* Indicate failure */
}
}
return 0;
}
gpointer queue_dequeue(Queue *q) {
Node *node;
Node *tail;
Node *next;
if (mtx_lock(&(q->mutex)) != thrd_success) {
return NULL; /* Indicate failure */
}
else {
head = q->head;
tail = q->tail;
next = head->next;
data = next->data;
q->head = next;
g_slice_free(Node, head);
if (mtx_unlock(&(queue->mutex)) != thrd_success ) {
return NULL; /* Indicate failure */
}
}
return data;
}
|
...
The likelihood of having a race condition is low. Once the race condition occurs, the reading memory that has already been freed can lead to abnormal program termination or unintended information disclosure.
GuidelineRule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
CON39-C | Medium | unlikely | High | P2 | L3 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Bibliography
| [Apiki 2006] | "Lock-Free Programming on AMD Multi-Core System" |
| [Asgher 2000] | "Practical Lock-Free Buffers" |
| [Michael 2004] | "Hazard Pointers" |
...