-
Notifications
You must be signed in to change notification settings - Fork 27
Open
Description
Location: runtime/gc/concurrent-stack.c
Function: CC_stack_try_close()
Lines: 199-205
Bug Pattern: Lock acquired in loop, early break without unlock
The Code
bool CC_stack_try_close(CC_stack* stack, HM_chunkList removed) {
// First, do a quick check to avoid taking locks unnecessarily
bool allEmpty = TRUE;
for (size_t i = 0; i < stack->numStacks; i++) {
HM_chunkList thisBag = &(stack->stacks[i].storage);
if (NULL != thisBag->firstChunk) {
allEmpty = FALSE;
break;
}
}
/** We checked that all are empty; now just have to confirm and close. This
* works by taking all the locks, verying that each is empty, and closing
* it. If any has been extended in the meantime, we have to abort.
*/
if (allEmpty) {
size_t i;
for (i = 0; i < stack->numStacks; i++) {
pthread_mutex_lock(&(stack->stacks[i].mutex)); // Line 200: Lock acquired
HM_chunkList thisBag = &(stack->stacks[i].storage);
if (NULL != thisBag->firstChunk)
break; // Line 203: BUG - breaks WITHOUT unlocking lock i!
stack->stacks[i].isClosed = TRUE;
}
if (i == stack->numStacks) {
// success! unlock everything and return.
stack->allClosed = TRUE;
for (size_t j = 0; j < i; j++) {
pthread_mutex_unlock(&(stack->stacks[j].mutex));
}
return TRUE;
}
// Otherwise, we failed to close. Unlock everything before continuing.
for (size_t j = 0; j < i; j++) { // Line 217: Only unlocks 0..i-1
pthread_mutex_unlock(&(stack->stacks[j].mutex));
}
// BUG: Lock i is still held!
}
/** If we reach here, then there is at least one bag which is non-empty,
* and we're currently holding no locks. Proceed by taking a batch of
* elements and returning them.
*/
for (size_t i = 0; i < stack->numStacks; i++) {
HM_chunkList thisBag = &(stack->stacks[i].storage);
if (NULL != HM_getChunkListFirstChunk(thisBag)) {
pthread_mutex_lock(&(stack->stacks[i].mutex));
HM_chunk chunk = HM_getChunkListFirstChunk(thisBag);
HM_unlinkChunk(thisBag, chunk);
pthread_mutex_unlock(&(stack->stacks[i].mutex));
HM_appendChunk(removed, chunk);
}
}
return FALSE;
}The Problem
- Line 200:
pthread_mutex_lock(&(stack->stacks[i].mutex))- Lockiacquired - Line 202: Check finds that
thisBag->firstChunk != NULL - Line 203: Loop exits via
break- Lockiis NOT released - Line 217: Unlock loop only releases locks
0throughi-1 - Result: Lock
iremains held indefinitely
Why It's a Bug
When the condition NULL != thisBag->firstChunk is true after acquiring lock i:
- The
breakstatement exits the loop immediately - Lock
iacquired at line 200 is NEVER released - The unlock loop at lines 217-219 only releases locks
0..i-1(not locki) - Lock
iremains held until process termination - Any future attempt to acquire lock
iwill block forever
Metadata
Metadata
Assignees
Labels
No labels