DF-1662 / harness.c
/* DF-1662: i915_gem_context destroy ioctl double-close race. * Lookup OUTSIDE struct_mutex, then mutex_lock, then __destroy_hw_context. * Two threads sharing fd race both past lookup (both get a ctx ref), * both enter __destroy_hw_context, second hits GEM_BUG_ON(is_closed) * (panic on debug) or runs full close + put (refcount underflow on prod). * * Harness: simulate the race; patched version takes mutex FIRST. */ #include <stdio.h> #include <stdint.h> #include <pthread.h> #include <stdlib.h> #include <string.h> static int fixed = 0; static int mutex_held; static int is_closed; static int refcount; static int close_count; static int bug_count; static void mutex_lock(void) { while (__sync_lock_test_and_set(&mutex_held, 1)) { /* spin */ } } static void mutex_unlock(void) { __sync_lock_release(&mutex_held); } static void context_close(void) { close_count++; if (is_closed) { /* GEM_BUG_ON(is_closed) - panics on debug builds */ bug_count++; } is_closed = 1; } /* simulates i915_gem_context_destroy_ioctl body */ static void *destroy_thread(void *arg) { /* both threads do ctx = lookup(); refcount++ */ __sync_fetch_and_add(&refcount, 1); if (fixed) { mutex_lock(); if (is_closed) { mutex_unlock(); return NULL; } context_close(); mutex_unlock(); } else { /* buggy: lookup outside mutex */ mutex_lock(); context_close(); /* re-entered by both threads */ mutex_unlock(); } return NULL; } int main(int argc, char **argv) { if (argc > 1 && !strcmp(argv[1], "--fixed")) fixed = 1; refcount = 1; is_closed = 0; close_count = 0; bug_count = 0; pthread_t t1, t2; pthread_create(&t1, NULL, destroy_thread, NULL); pthread_create(&t2, NULL, destroy_thread, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); if (bug_count > 0) printf("RESULT: BUGGY - GEM_BUG_ON(is_closed) hit %d time(s)\n", bug_count); else printf("RESULT: PATCHED - close ran once, no race\n"); return 0; } |