Skip to content

Commit 624826b

Browse files
committed
fix audisp/queue.c wrap resize
Fixed the wrapped-resize data loss in audisp/queue.c:367. The grow path now allocates a fresh queue, copies live entries in FIFO order starting from q_last, then resets q_last to 0 and q_next to the live count. That preserves dequeue order even when the old ring had wrapped. Added a regression test in audisp/test/test-queue.c:242. It fills a 100-slot queue, dequeues 80 entries, enqueues 20 more to force wrap, resizes to 200, and verifies that entries 80..119 are still dequeued in order.
1 parent 73339a6 commit 624826b

2 files changed

Lines changed: 118 additions & 7 deletions

File tree

audisp/queue.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -368,20 +368,44 @@ void increase_queue_depth(unsigned int size)
368368
{
369369
pthread_mutex_lock(&queue_lock);
370370
if (size > q_depth) {
371-
unsigned int i;
372-
void *tmp_q;
371+
volatile event_t **tmp_q;
372+
unsigned int count, i, old_depth, old_last;
373373

374-
tmp_q = realloc(q, size * sizeof(event_t *));
374+
old_depth = q_depth;
375+
count = AUDIT_ATOMIC_LOAD(currently_used);
376+
#ifdef HAVE_ATOMIC
377+
old_last = atomic_load_explicit(&q_last,
378+
memory_order_relaxed) % old_depth;
379+
#else
380+
old_last = q_last % old_depth;
381+
#endif
382+
383+
tmp_q = calloc(size, sizeof(event_t *));
375384
if (tmp_q == NULL) {
376385
fprintf(stderr, "Out of Memory. Check %s file, %d line",
377386
__FILE__, __LINE__);
378387
pthread_mutex_unlock(&queue_lock);
379388
return;
380389
}
390+
391+
/*
392+
* Preserve FIFO order across a grow. Once the ring has wrapped,
393+
* simply extending the backing array strands the entries stored
394+
* at the start of the old allocation.
395+
*/
396+
for (i = 0; i < count; i++)
397+
tmp_q[i] = q[(old_last + i) % old_depth];
398+
399+
free((void *)q);
381400
q = tmp_q;
382-
for (i=q_depth; i<size; i++)
383-
q[i] = NULL;
384401
q_depth = size;
402+
#ifdef HAVE_ATOMIC
403+
atomic_store_explicit(&q_last, 0, memory_order_relaxed);
404+
atomic_store_explicit(&q_next, count, memory_order_relaxed);
405+
#else
406+
q_last = 0;
407+
q_next = count;
408+
#endif
385409
overflowed = 0;
386410
}
387411
pthread_mutex_unlock(&queue_lock);
@@ -456,4 +480,3 @@ int queue_overflowed_p(void)
456480
{
457481
return overflowed;
458482
}
459-

audisp/test/test-queue.c

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,93 @@ static int persist_test(const char *logfile)
239239
return rc;
240240
}
241241

242+
static int resize_wrap_test(void)
243+
{
244+
struct disp_conf conf;
245+
event_t *e = NULL;
246+
char buf[32];
247+
int i, rc = 1;
248+
249+
memset(&conf, 0, sizeof(conf));
250+
conf.overflow_action = O_IGNORE;
251+
252+
if (init_queue(100)) {
253+
fprintf(stderr, "resize_wrap_test: init_queue failed\n");
254+
return rc;
255+
}
256+
257+
/* Fill the ring so the next enqueue sequence can wrap it. */
258+
for (i = 0; i < 100; i++) {
259+
snprintf(buf, sizeof(buf), "event-%03d", i);
260+
e = make_event(buf);
261+
if (!e || enqueue(e, &conf)) {
262+
fprintf(stderr, "resize_wrap_test: enqueue failed\n");
263+
goto out_q;
264+
}
265+
e = NULL;
266+
}
267+
268+
for (i = 0; i < 80; i++) {
269+
snprintf(buf, sizeof(buf), "event-%03d", i);
270+
e = dequeue();
271+
if (!e) {
272+
fprintf(stderr, "resize_wrap_test: queue underflow\n");
273+
goto out_q;
274+
}
275+
if (strcmp(e->data, buf) != 0) {
276+
fprintf(stderr, "resize_wrap_test: initial data mismatch\n");
277+
goto out_free;
278+
}
279+
free(e);
280+
e = NULL;
281+
}
282+
283+
/* Add more entries so the live data spans the end and start. */
284+
for (i = 100; i < 120; i++) {
285+
snprintf(buf, sizeof(buf), "event-%03d", i);
286+
e = make_event(buf);
287+
if (!e || enqueue(e, &conf)) {
288+
fprintf(stderr,
289+
"resize_wrap_test: wrapped enqueue failed\n");
290+
goto out_q;
291+
}
292+
e = NULL;
293+
}
294+
295+
/* Growing a wrapped ring must not strand the entries at slot 0. */
296+
increase_queue_depth(200);
297+
298+
/* The consumer should still see the wrapped entries in FIFO order. */
299+
for (i = 80; i < 120; i++) {
300+
snprintf(buf, sizeof(buf), "event-%03d", i);
301+
e = dequeue();
302+
if (!e) {
303+
fprintf(stderr,
304+
"resize_wrap_test: queue underflow after resize\n");
305+
goto out_q;
306+
}
307+
if (strcmp(e->data, buf) != 0) {
308+
fprintf(stderr,
309+
"resize_wrap_test: data mismatch after resize\n");
310+
goto out_free;
311+
}
312+
free(e);
313+
e = NULL;
314+
}
315+
316+
if (queue_current_depth() != 0) {
317+
fprintf(stderr, "resize_wrap_test: depth not zero\n");
318+
goto out_q;
319+
}
320+
321+
rc = 0;
322+
out_free:
323+
free(e);
324+
out_q:
325+
destroy_queue();
326+
return rc;
327+
}
328+
242329
int main(void)
243330
{
244331
const char *srcdir = getenv("srcdir") ? getenv("srcdir") : ".";
@@ -247,10 +334,11 @@ int main(void)
247334

248335
if (basic_test(path))
249336
return 1;
337+
if (resize_wrap_test())
338+
return 1;
250339
if (persist_test(path))
251340
return 1;
252341
if (concurrency_test(path))
253342
return 1;
254343
return 0;
255344
}
256-

0 commit comments

Comments
 (0)