// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc -verify %s // typedef __SIZE_TYPE__ size_t; typedef enum memory_order { memory_order_relaxed = __ATOMIC_RELAXED, } memory_order; void *calloc(size_t, size_t); void free(void *); struct SomeData { int i; _Atomic int ref; }; static struct SomeData *alloc_data(void) { struct SomeData *data = calloc(sizeof(*data), 1); __c11_atomic_store(&data->ref, 2, memory_order_relaxed); return data; } static void put_data(struct SomeData *data) { if (__c11_atomic_fetch_sub(&data->ref, 1, memory_order_relaxed) == 1) free(data); } static int dec_refcounter(struct SomeData *data) { return __c11_atomic_fetch_sub(&data->ref, 1, memory_order_relaxed) == 1; } static void put_data_nested(struct SomeData *data) { if (dec_refcounter(data)) free(data); } static void put_data_uncond(struct SomeData *data) { free(data); } static void put_data_unrelated_atomic(struct SomeData *data) { free(data); __c11_atomic_fetch_sub(&data->ref, 1, memory_order_relaxed); } void test_no_uaf(void) { struct SomeData *data = alloc_data(); put_data(data); data->i += 1; // no warning } void test_no_uaf_nested(void) { struct SomeData *data = alloc_data(); put_data_nested(data); data->i += 1; // no warning } void test_uaf(void) { struct SomeData *data = alloc_data(); put_data_uncond(data); data->i += 1; // expected-warning{{Use of memory after it is released}} } void test_no_uaf_atomic_after(void) { struct SomeData *data = alloc_data(); put_data_unrelated_atomic(data); data->i += 1; // expected-warning{{Use of memory after it is released}} }