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

tag_check.c (8832B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <stdlib.h>
      3#include <assert.h>
      4#include <stdio.h>
      5#include <string.h>
      6
      7#include <linux/slab.h>
      8#include <linux/radix-tree.h>
      9
     10#include "test.h"
     11
     12
     13static void
     14__simple_checks(struct radix_tree_root *tree, unsigned long index, int tag)
     15{
     16	unsigned long first = 0;
     17	int ret;
     18
     19	item_check_absent(tree, index);
     20	assert(item_tag_get(tree, index, tag) == 0);
     21
     22	item_insert(tree, index);
     23	assert(item_tag_get(tree, index, tag) == 0);
     24	item_tag_set(tree, index, tag);
     25	ret = item_tag_get(tree, index, tag);
     26	assert(ret != 0);
     27	ret = tag_tagged_items(tree, first, ~0UL, 10, tag, !tag);
     28	assert(ret == 1);
     29	ret = item_tag_get(tree, index, !tag);
     30	assert(ret != 0);
     31	ret = item_delete(tree, index);
     32	assert(ret != 0);
     33	item_insert(tree, index);
     34	ret = item_tag_get(tree, index, tag);
     35	assert(ret == 0);
     36	ret = item_delete(tree, index);
     37	assert(ret != 0);
     38	ret = item_delete(tree, index);
     39	assert(ret == 0);
     40}
     41
     42void simple_checks(void)
     43{
     44	unsigned long index;
     45	RADIX_TREE(tree, GFP_KERNEL);
     46
     47	for (index = 0; index < 10000; index++) {
     48		__simple_checks(&tree, index, 0);
     49		__simple_checks(&tree, index, 1);
     50	}
     51	verify_tag_consistency(&tree, 0);
     52	verify_tag_consistency(&tree, 1);
     53	printv(2, "before item_kill_tree: %d allocated\n", nr_allocated);
     54	item_kill_tree(&tree);
     55	rcu_barrier();
     56	printv(2, "after item_kill_tree: %d allocated\n", nr_allocated);
     57}
     58
     59/*
     60 * Check that tags propagate correctly when extending a tree.
     61 */
     62static void extend_checks(void)
     63{
     64	RADIX_TREE(tree, GFP_KERNEL);
     65
     66	item_insert(&tree, 43);
     67	assert(item_tag_get(&tree, 43, 0) == 0);
     68	item_tag_set(&tree, 43, 0);
     69	assert(item_tag_get(&tree, 43, 0) == 1);
     70	item_insert(&tree, 1000000);
     71	assert(item_tag_get(&tree, 43, 0) == 1);
     72
     73	item_insert(&tree, 0);
     74	item_tag_set(&tree, 0, 0);
     75	item_delete(&tree, 1000000);
     76	assert(item_tag_get(&tree, 43, 0) != 0);
     77	item_delete(&tree, 43);
     78	assert(item_tag_get(&tree, 43, 0) == 0);	/* crash */
     79	assert(item_tag_get(&tree, 0, 0) == 1);
     80
     81	verify_tag_consistency(&tree, 0);
     82
     83	item_kill_tree(&tree);
     84}
     85
     86/*
     87 * Check that tags propagate correctly when contracting a tree.
     88 */
     89static void contract_checks(void)
     90{
     91	struct item *item;
     92	int tmp;
     93	RADIX_TREE(tree, GFP_KERNEL);
     94
     95	tmp = 1<<RADIX_TREE_MAP_SHIFT;
     96	item_insert(&tree, tmp);
     97	item_insert(&tree, tmp+1);
     98	item_tag_set(&tree, tmp, 0);
     99	item_tag_set(&tree, tmp, 1);
    100	item_tag_set(&tree, tmp+1, 0);
    101	item_delete(&tree, tmp+1);
    102	item_tag_clear(&tree, tmp, 1);
    103
    104	assert(radix_tree_gang_lookup_tag(&tree, (void **)&item, 0, 1, 0) == 1);
    105	assert(radix_tree_gang_lookup_tag(&tree, (void **)&item, 0, 1, 1) == 0);
    106
    107	assert(item_tag_get(&tree, tmp, 0) == 1);
    108	assert(item_tag_get(&tree, tmp, 1) == 0);
    109
    110	verify_tag_consistency(&tree, 0);
    111	item_kill_tree(&tree);
    112}
    113
    114/*
    115 * Stupid tag thrasher
    116 *
    117 * Create a large linear array corresponding to the tree.   Each element in
    118 * the array is coherent with each node in the tree
    119 */
    120
    121enum {
    122	NODE_ABSENT = 0,
    123	NODE_PRESENT = 1,
    124	NODE_TAGGED = 2,
    125};
    126
    127#define THRASH_SIZE		(1000 * 1000)
    128#define N 127
    129#define BATCH	33
    130
    131static void gang_check(struct radix_tree_root *tree,
    132			char *thrash_state, int tag)
    133{
    134	struct item *items[BATCH];
    135	int nr_found;
    136	unsigned long index = 0;
    137	unsigned long last_index = 0;
    138
    139	while ((nr_found = radix_tree_gang_lookup_tag(tree, (void **)items,
    140					index, BATCH, tag))) {
    141		int i;
    142
    143		for (i = 0; i < nr_found; i++) {
    144			struct item *item = items[i];
    145
    146			while (last_index < item->index) {
    147				assert(thrash_state[last_index] != NODE_TAGGED);
    148				last_index++;
    149			}
    150			assert(thrash_state[last_index] == NODE_TAGGED);
    151			last_index++;
    152		}
    153		index = items[nr_found - 1]->index + 1;
    154	}
    155}
    156
    157static void do_thrash(struct radix_tree_root *tree, char *thrash_state, int tag)
    158{
    159	int insert_chunk;
    160	int delete_chunk;
    161	int tag_chunk;
    162	int untag_chunk;
    163	int total_tagged = 0;
    164	int total_present = 0;
    165
    166	for (insert_chunk = 1; insert_chunk < THRASH_SIZE; insert_chunk *= N)
    167	for (delete_chunk = 1; delete_chunk < THRASH_SIZE; delete_chunk *= N)
    168	for (tag_chunk = 1; tag_chunk < THRASH_SIZE; tag_chunk *= N)
    169	for (untag_chunk = 1; untag_chunk < THRASH_SIZE; untag_chunk *= N) {
    170		int i;
    171		unsigned long index;
    172		int nr_inserted = 0;
    173		int nr_deleted = 0;
    174		int nr_tagged = 0;
    175		int nr_untagged = 0;
    176		int actual_total_tagged;
    177		int actual_total_present;
    178
    179		for (i = 0; i < insert_chunk; i++) {
    180			index = rand() % THRASH_SIZE;
    181			if (thrash_state[index] != NODE_ABSENT)
    182				continue;
    183			item_check_absent(tree, index);
    184			item_insert(tree, index);
    185			assert(thrash_state[index] != NODE_PRESENT);
    186			thrash_state[index] = NODE_PRESENT;
    187			nr_inserted++;
    188			total_present++;
    189		}
    190
    191		for (i = 0; i < delete_chunk; i++) {
    192			index = rand() % THRASH_SIZE;
    193			if (thrash_state[index] == NODE_ABSENT)
    194				continue;
    195			item_check_present(tree, index);
    196			if (item_tag_get(tree, index, tag)) {
    197				assert(thrash_state[index] == NODE_TAGGED);
    198				total_tagged--;
    199			} else {
    200				assert(thrash_state[index] == NODE_PRESENT);
    201			}
    202			item_delete(tree, index);
    203			assert(thrash_state[index] != NODE_ABSENT);
    204			thrash_state[index] = NODE_ABSENT;
    205			nr_deleted++;
    206			total_present--;
    207		}
    208
    209		for (i = 0; i < tag_chunk; i++) {
    210			index = rand() % THRASH_SIZE;
    211			if (thrash_state[index] != NODE_PRESENT) {
    212				if (item_lookup(tree, index))
    213					assert(item_tag_get(tree, index, tag));
    214				continue;
    215			}
    216			item_tag_set(tree, index, tag);
    217			item_tag_set(tree, index, tag);
    218			assert(thrash_state[index] != NODE_TAGGED);
    219			thrash_state[index] = NODE_TAGGED;
    220			nr_tagged++;
    221			total_tagged++;
    222		}
    223
    224		for (i = 0; i < untag_chunk; i++) {
    225			index = rand() % THRASH_SIZE;
    226			if (thrash_state[index] != NODE_TAGGED)
    227				continue;
    228			item_check_present(tree, index);
    229			assert(item_tag_get(tree, index, tag));
    230			item_tag_clear(tree, index, tag);
    231			item_tag_clear(tree, index, tag);
    232			assert(thrash_state[index] != NODE_PRESENT);
    233			thrash_state[index] = NODE_PRESENT;
    234			nr_untagged++;
    235			total_tagged--;
    236		}
    237
    238		actual_total_tagged = 0;
    239		actual_total_present = 0;
    240		for (index = 0; index < THRASH_SIZE; index++) {
    241			switch (thrash_state[index]) {
    242			case NODE_ABSENT:
    243				item_check_absent(tree, index);
    244				break;
    245			case NODE_PRESENT:
    246				item_check_present(tree, index);
    247				assert(!item_tag_get(tree, index, tag));
    248				actual_total_present++;
    249				break;
    250			case NODE_TAGGED:
    251				item_check_present(tree, index);
    252				assert(item_tag_get(tree, index, tag));
    253				actual_total_present++;
    254				actual_total_tagged++;
    255				break;
    256			}
    257		}
    258
    259		gang_check(tree, thrash_state, tag);
    260
    261		printv(2, "%d(%d) %d(%d) %d(%d) %d(%d) / "
    262				"%d(%d) present, %d(%d) tagged\n",
    263			insert_chunk, nr_inserted,
    264			delete_chunk, nr_deleted,
    265			tag_chunk, nr_tagged,
    266			untag_chunk, nr_untagged,
    267			total_present, actual_total_present,
    268			total_tagged, actual_total_tagged);
    269	}
    270}
    271
    272static void thrash_tags(void)
    273{
    274	RADIX_TREE(tree, GFP_KERNEL);
    275	char *thrash_state;
    276
    277	thrash_state = malloc(THRASH_SIZE);
    278	memset(thrash_state, 0, THRASH_SIZE);
    279
    280	do_thrash(&tree, thrash_state, 0);
    281
    282	verify_tag_consistency(&tree, 0);
    283	item_kill_tree(&tree);
    284	free(thrash_state);
    285}
    286
    287static void leak_check(void)
    288{
    289	RADIX_TREE(tree, GFP_KERNEL);
    290
    291	item_insert(&tree, 1000000);
    292	item_delete(&tree, 1000000);
    293	item_kill_tree(&tree);
    294}
    295
    296static void __leak_check(void)
    297{
    298	RADIX_TREE(tree, GFP_KERNEL);
    299
    300	printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
    301	item_insert(&tree, 1000000);
    302	printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
    303	item_delete(&tree, 1000000);
    304	printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
    305	item_kill_tree(&tree);
    306	printv(2, "%d: nr_allocated=%d\n", __LINE__, nr_allocated);
    307}
    308
    309static void single_check(void)
    310{
    311	struct item *items[BATCH];
    312	RADIX_TREE(tree, GFP_KERNEL);
    313	int ret;
    314	unsigned long first = 0;
    315
    316	item_insert(&tree, 0);
    317	item_tag_set(&tree, 0, 0);
    318	ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 0);
    319	assert(ret == 1);
    320	ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 1, BATCH, 0);
    321	assert(ret == 0);
    322	verify_tag_consistency(&tree, 0);
    323	verify_tag_consistency(&tree, 1);
    324	ret = tag_tagged_items(&tree, first, 10, 10, XA_MARK_0, XA_MARK_1);
    325	assert(ret == 1);
    326	ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 1);
    327	assert(ret == 1);
    328	item_tag_clear(&tree, 0, 0);
    329	ret = radix_tree_gang_lookup_tag(&tree, (void **)items, 0, BATCH, 0);
    330	assert(ret == 0);
    331	item_kill_tree(&tree);
    332}
    333
    334void tag_check(void)
    335{
    336	single_check();
    337	extend_checks();
    338	contract_checks();
    339	rcu_barrier();
    340	printv(2, "after extend_checks: %d allocated\n", nr_allocated);
    341	__leak_check();
    342	leak_check();
    343	rcu_barrier();
    344	printv(2, "after leak_check: %d allocated\n", nr_allocated);
    345	simple_checks();
    346	rcu_barrier();
    347	printv(2, "after simple_checks: %d allocated\n", nr_allocated);
    348	thrash_tags();
    349	rcu_barrier();
    350	printv(2, "after thrash_tags: %d allocated\n", nr_allocated);
    351}