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

ibpkey.c (5702B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Pkey table
      4 *
      5 * SELinux must keep a mapping of Infinband PKEYs to labels/SIDs.  This
      6 * mapping is maintained as part of the normal policy but a fast cache is
      7 * needed to reduce the lookup overhead.
      8 *
      9 * This code is heavily based on the "netif" and "netport" concept originally
     10 * developed by
     11 * James Morris <jmorris@redhat.com> and
     12 * Paul Moore <paul@paul-moore.com>
     13 *   (see security/selinux/netif.c and security/selinux/netport.c for more
     14 *   information)
     15 */
     16
     17/*
     18 * (c) Mellanox Technologies, 2016
     19 */
     20
     21#include <linux/types.h>
     22#include <linux/rcupdate.h>
     23#include <linux/list.h>
     24#include <linux/spinlock.h>
     25
     26#include "ibpkey.h"
     27#include "objsec.h"
     28
     29#define SEL_PKEY_HASH_SIZE       256
     30#define SEL_PKEY_HASH_BKT_LIMIT   16
     31
     32struct sel_ib_pkey_bkt {
     33	int size;
     34	struct list_head list;
     35};
     36
     37struct sel_ib_pkey {
     38	struct pkey_security_struct psec;
     39	struct list_head list;
     40	struct rcu_head rcu;
     41};
     42
     43static DEFINE_SPINLOCK(sel_ib_pkey_lock);
     44static struct sel_ib_pkey_bkt sel_ib_pkey_hash[SEL_PKEY_HASH_SIZE];
     45
     46/**
     47 * sel_ib_pkey_hashfn - Hashing function for the pkey table
     48 * @pkey: pkey number
     49 *
     50 * Description:
     51 * This is the hashing function for the pkey table, it returns the bucket
     52 * number for the given pkey.
     53 *
     54 */
     55static unsigned int sel_ib_pkey_hashfn(u16 pkey)
     56{
     57	return (pkey & (SEL_PKEY_HASH_SIZE - 1));
     58}
     59
     60/**
     61 * sel_ib_pkey_find - Search for a pkey record
     62 * @subnet_prefix: subnet_prefix
     63 * @pkey_num: pkey_num
     64 *
     65 * Description:
     66 * Search the pkey table and return the matching record.  If an entry
     67 * can not be found in the table return NULL.
     68 *
     69 */
     70static struct sel_ib_pkey *sel_ib_pkey_find(u64 subnet_prefix, u16 pkey_num)
     71{
     72	unsigned int idx;
     73	struct sel_ib_pkey *pkey;
     74
     75	idx = sel_ib_pkey_hashfn(pkey_num);
     76	list_for_each_entry_rcu(pkey, &sel_ib_pkey_hash[idx].list, list) {
     77		if (pkey->psec.pkey == pkey_num &&
     78		    pkey->psec.subnet_prefix == subnet_prefix)
     79			return pkey;
     80	}
     81
     82	return NULL;
     83}
     84
     85/**
     86 * sel_ib_pkey_insert - Insert a new pkey into the table
     87 * @pkey: the new pkey record
     88 *
     89 * Description:
     90 * Add a new pkey record to the hash table.
     91 *
     92 */
     93static void sel_ib_pkey_insert(struct sel_ib_pkey *pkey)
     94{
     95	unsigned int idx;
     96
     97	/* we need to impose a limit on the growth of the hash table so check
     98	 * this bucket to make sure it is within the specified bounds
     99	 */
    100	idx = sel_ib_pkey_hashfn(pkey->psec.pkey);
    101	list_add_rcu(&pkey->list, &sel_ib_pkey_hash[idx].list);
    102	if (sel_ib_pkey_hash[idx].size == SEL_PKEY_HASH_BKT_LIMIT) {
    103		struct sel_ib_pkey *tail;
    104
    105		tail = list_entry(
    106			rcu_dereference_protected(
    107				list_tail_rcu(&sel_ib_pkey_hash[idx].list),
    108				lockdep_is_held(&sel_ib_pkey_lock)),
    109			struct sel_ib_pkey, list);
    110		list_del_rcu(&tail->list);
    111		kfree_rcu(tail, rcu);
    112	} else {
    113		sel_ib_pkey_hash[idx].size++;
    114	}
    115}
    116
    117/**
    118 * sel_ib_pkey_sid_slow - Lookup the SID of a pkey using the policy
    119 * @subnet_prefix: subnet prefix
    120 * @pkey_num: pkey number
    121 * @sid: pkey SID
    122 *
    123 * Description:
    124 * This function determines the SID of a pkey by querying the security
    125 * policy.  The result is added to the pkey table to speedup future
    126 * queries.  Returns zero on success, negative values on failure.
    127 *
    128 */
    129static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)
    130{
    131	int ret;
    132	struct sel_ib_pkey *pkey;
    133	struct sel_ib_pkey *new = NULL;
    134	unsigned long flags;
    135
    136	spin_lock_irqsave(&sel_ib_pkey_lock, flags);
    137	pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
    138	if (pkey) {
    139		*sid = pkey->psec.sid;
    140		spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
    141		return 0;
    142	}
    143
    144	ret = security_ib_pkey_sid(&selinux_state, subnet_prefix, pkey_num,
    145				   sid);
    146	if (ret)
    147		goto out;
    148
    149	/* If this memory allocation fails still return 0. The SID
    150	 * is valid, it just won't be added to the cache.
    151	 */
    152	new = kzalloc(sizeof(*new), GFP_ATOMIC);
    153	if (!new) {
    154		ret = -ENOMEM;
    155		goto out;
    156	}
    157
    158	new->psec.subnet_prefix = subnet_prefix;
    159	new->psec.pkey = pkey_num;
    160	new->psec.sid = *sid;
    161	sel_ib_pkey_insert(new);
    162
    163out:
    164	spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
    165	return ret;
    166}
    167
    168/**
    169 * sel_ib_pkey_sid - Lookup the SID of a PKEY
    170 * @subnet_prefix: subnet_prefix
    171 * @pkey_num: pkey number
    172 * @sid: pkey SID
    173 *
    174 * Description:
    175 * This function determines the SID of a PKEY using the fastest method
    176 * possible.  First the pkey table is queried, but if an entry can't be found
    177 * then the policy is queried and the result is added to the table to speedup
    178 * future queries.  Returns zero on success, negative values on failure.
    179 *
    180 */
    181int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *sid)
    182{
    183	struct sel_ib_pkey *pkey;
    184
    185	rcu_read_lock();
    186	pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
    187	if (pkey) {
    188		*sid = pkey->psec.sid;
    189		rcu_read_unlock();
    190		return 0;
    191	}
    192	rcu_read_unlock();
    193
    194	return sel_ib_pkey_sid_slow(subnet_prefix, pkey_num, sid);
    195}
    196
    197/**
    198 * sel_ib_pkey_flush - Flush the entire pkey table
    199 *
    200 * Description:
    201 * Remove all entries from the pkey table
    202 *
    203 */
    204void sel_ib_pkey_flush(void)
    205{
    206	unsigned int idx;
    207	struct sel_ib_pkey *pkey, *pkey_tmp;
    208	unsigned long flags;
    209
    210	spin_lock_irqsave(&sel_ib_pkey_lock, flags);
    211	for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) {
    212		list_for_each_entry_safe(pkey, pkey_tmp,
    213					 &sel_ib_pkey_hash[idx].list, list) {
    214			list_del_rcu(&pkey->list);
    215			kfree_rcu(pkey, rcu);
    216		}
    217		sel_ib_pkey_hash[idx].size = 0;
    218	}
    219	spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
    220}
    221
    222static __init int sel_ib_pkey_init(void)
    223{
    224	int iter;
    225
    226	if (!selinux_enabled_boot)
    227		return 0;
    228
    229	for (iter = 0; iter < SEL_PKEY_HASH_SIZE; iter++) {
    230		INIT_LIST_HEAD(&sel_ib_pkey_hash[iter].list);
    231		sel_ib_pkey_hash[iter].size = 0;
    232	}
    233
    234	return 0;
    235}
    236
    237subsys_initcall(sel_ib_pkey_init);