cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ref_tracker.c (4105B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2#include <linux/export.h>
      3#include <linux/ref_tracker.h>
      4#include <linux/slab.h>
      5#include <linux/stacktrace.h>
      6#include <linux/stackdepot.h>
      7
      8#define REF_TRACKER_STACK_ENTRIES 16
      9
     10struct ref_tracker {
     11	struct list_head	head;   /* anchor into dir->list or dir->quarantine */
     12	bool			dead;
     13	depot_stack_handle_t	alloc_stack_handle;
     14	depot_stack_handle_t	free_stack_handle;
     15};
     16
     17void ref_tracker_dir_exit(struct ref_tracker_dir *dir)
     18{
     19	struct ref_tracker *tracker, *n;
     20	unsigned long flags;
     21	bool leak = false;
     22
     23	dir->dead = true;
     24	spin_lock_irqsave(&dir->lock, flags);
     25	list_for_each_entry_safe(tracker, n, &dir->quarantine, head) {
     26		list_del(&tracker->head);
     27		kfree(tracker);
     28		dir->quarantine_avail++;
     29	}
     30	list_for_each_entry_safe(tracker, n, &dir->list, head) {
     31		pr_err("leaked reference.\n");
     32		if (tracker->alloc_stack_handle)
     33			stack_depot_print(tracker->alloc_stack_handle);
     34		leak = true;
     35		list_del(&tracker->head);
     36		kfree(tracker);
     37	}
     38	spin_unlock_irqrestore(&dir->lock, flags);
     39	WARN_ON_ONCE(leak);
     40	WARN_ON_ONCE(refcount_read(&dir->untracked) != 1);
     41	WARN_ON_ONCE(refcount_read(&dir->no_tracker) != 1);
     42}
     43EXPORT_SYMBOL(ref_tracker_dir_exit);
     44
     45void ref_tracker_dir_print(struct ref_tracker_dir *dir,
     46			   unsigned int display_limit)
     47{
     48	struct ref_tracker *tracker;
     49	unsigned long flags;
     50	unsigned int i = 0;
     51
     52	spin_lock_irqsave(&dir->lock, flags);
     53	list_for_each_entry(tracker, &dir->list, head) {
     54		if (i < display_limit) {
     55			pr_err("leaked reference.\n");
     56			if (tracker->alloc_stack_handle)
     57				stack_depot_print(tracker->alloc_stack_handle);
     58			i++;
     59		} else {
     60			break;
     61		}
     62	}
     63	spin_unlock_irqrestore(&dir->lock, flags);
     64}
     65EXPORT_SYMBOL(ref_tracker_dir_print);
     66
     67int ref_tracker_alloc(struct ref_tracker_dir *dir,
     68		      struct ref_tracker **trackerp,
     69		      gfp_t gfp)
     70{
     71	unsigned long entries[REF_TRACKER_STACK_ENTRIES];
     72	struct ref_tracker *tracker;
     73	unsigned int nr_entries;
     74	gfp_t gfp_mask = gfp;
     75	unsigned long flags;
     76
     77	WARN_ON_ONCE(dir->dead);
     78
     79	if (!trackerp) {
     80		refcount_inc(&dir->no_tracker);
     81		return 0;
     82	}
     83	if (gfp & __GFP_DIRECT_RECLAIM)
     84		gfp_mask |= __GFP_NOFAIL;
     85	*trackerp = tracker = kzalloc(sizeof(*tracker), gfp_mask);
     86	if (unlikely(!tracker)) {
     87		pr_err_once("memory allocation failure, unreliable refcount tracker.\n");
     88		refcount_inc(&dir->untracked);
     89		return -ENOMEM;
     90	}
     91	nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
     92	tracker->alloc_stack_handle = stack_depot_save(entries, nr_entries, gfp);
     93
     94	spin_lock_irqsave(&dir->lock, flags);
     95	list_add(&tracker->head, &dir->list);
     96	spin_unlock_irqrestore(&dir->lock, flags);
     97	return 0;
     98}
     99EXPORT_SYMBOL_GPL(ref_tracker_alloc);
    100
    101int ref_tracker_free(struct ref_tracker_dir *dir,
    102		     struct ref_tracker **trackerp)
    103{
    104	unsigned long entries[REF_TRACKER_STACK_ENTRIES];
    105	depot_stack_handle_t stack_handle;
    106	struct ref_tracker *tracker;
    107	unsigned int nr_entries;
    108	unsigned long flags;
    109
    110	WARN_ON_ONCE(dir->dead);
    111
    112	if (!trackerp) {
    113		refcount_dec(&dir->no_tracker);
    114		return 0;
    115	}
    116	tracker = *trackerp;
    117	if (!tracker) {
    118		refcount_dec(&dir->untracked);
    119		return -EEXIST;
    120	}
    121	nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
    122	stack_handle = stack_depot_save(entries, nr_entries, GFP_ATOMIC);
    123
    124	spin_lock_irqsave(&dir->lock, flags);
    125	if (tracker->dead) {
    126		pr_err("reference already released.\n");
    127		if (tracker->alloc_stack_handle) {
    128			pr_err("allocated in:\n");
    129			stack_depot_print(tracker->alloc_stack_handle);
    130		}
    131		if (tracker->free_stack_handle) {
    132			pr_err("freed in:\n");
    133			stack_depot_print(tracker->free_stack_handle);
    134		}
    135		spin_unlock_irqrestore(&dir->lock, flags);
    136		WARN_ON_ONCE(1);
    137		return -EINVAL;
    138	}
    139	tracker->dead = true;
    140
    141	tracker->free_stack_handle = stack_handle;
    142
    143	list_move_tail(&tracker->head, &dir->quarantine);
    144	if (!dir->quarantine_avail) {
    145		tracker = list_first_entry(&dir->quarantine, struct ref_tracker, head);
    146		list_del(&tracker->head);
    147	} else {
    148		dir->quarantine_avail--;
    149		tracker = NULL;
    150	}
    151	spin_unlock_irqrestore(&dir->lock, flags);
    152
    153	kfree(tracker);
    154	return 0;
    155}
    156EXPORT_SYMBOL_GPL(ref_tracker_free);