root/security/selinux/ss/ebitmap.c
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Implementation of the extensible bitmap type.
 *
 * Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
 */
/*
 * Updated: Hewlett-Packard <paul@paul-moore.com>
 *          Added support to import/export the NetLabel category bitmap
 *          (c) Copyright Hewlett-Packard Development Company, L.P., 2006
 *
 * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com>
 *          Applied standard bit operations to improve bitmap scanning.
 */

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/jhash.h>
#include <net/netlabel.h>
#include "ebitmap.h"
#include "policydb.h"

#define BITS_PER_U64 ((u32)(sizeof(u64) * 8))

static struct kmem_cache *ebitmap_node_cachep __ro_after_init;

bool ebitmap_equal(const struct ebitmap *e1, const struct ebitmap *e2)
{
        const struct ebitmap_node *n1, *n2;

        if (e1->highbit != e2->highbit)
                return false;

        n1 = e1->node;
        n2 = e2->node;
        while (n1 && n2 && (n1->startbit == n2->startbit) &&
               !memcmp(n1->maps, n2->maps, EBITMAP_SIZE / 8)) {
                n1 = n1->next;
                n2 = n2->next;
        }

        if (n1 || n2)
                return false;

        return true;
}

int ebitmap_cpy(struct ebitmap *dst, const struct ebitmap *src)
{
        struct ebitmap_node *new, *prev;
        const struct ebitmap_node *n;

        ebitmap_init(dst);
        n = src->node;
        prev = NULL;
        while (n) {
                new = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
                if (!new) {
                        ebitmap_destroy(dst);
                        return -ENOMEM;
                }
                new->startbit = n->startbit;
                memcpy(new->maps, n->maps, EBITMAP_SIZE / 8);
                new->next = NULL;
                if (prev)
                        prev->next = new;
                else
                        dst->node = new;
                prev = new;
                n = n->next;
        }

        dst->highbit = src->highbit;
        return 0;
}

int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1,
                const struct ebitmap *e2)
{
        struct ebitmap_node *n;
        u32 bit;
        int rc;

        ebitmap_init(dst);

        ebitmap_for_each_positive_bit(e1, n, bit)
        {
                if (ebitmap_get_bit(e2, bit)) {
                        rc = ebitmap_set_bit(dst, bit, 1);
                        if (rc < 0)
                                return rc;
                }
        }
        return 0;
}

#ifdef CONFIG_NETLABEL
/**
 * ebitmap_netlbl_export - Export an ebitmap into a NetLabel category bitmap
 * @ebmap: the ebitmap to export
 * @catmap: the NetLabel category bitmap
 *
 * Description:
 * Export a SELinux extensibile bitmap into a NetLabel category bitmap.
 * Returns zero on success, negative values on error.
 *
 */
int ebitmap_netlbl_export(struct ebitmap *ebmap,
                          struct netlbl_lsm_catmap **catmap)
{
        struct ebitmap_node *e_iter = ebmap->node;
        unsigned long e_map;
        u32 offset;
        unsigned int iter;
        int rc;

        if (e_iter == NULL) {
                *catmap = NULL;
                return 0;
        }

        if (*catmap != NULL)
                netlbl_catmap_free(*catmap);
        *catmap = NULL;

        while (e_iter) {
                offset = e_iter->startbit;
                for (iter = 0; iter < EBITMAP_UNIT_NUMS; iter++) {
                        e_map = e_iter->maps[iter];
                        if (e_map != 0) {
                                rc = netlbl_catmap_setlong(catmap, offset,
                                                           e_map, GFP_ATOMIC);
                                if (rc != 0)
                                        goto netlbl_export_failure;
                        }
                        offset += EBITMAP_UNIT_SIZE;
                }
                e_iter = e_iter->next;
        }

        return 0;

netlbl_export_failure:
        netlbl_catmap_free(*catmap);
        return -ENOMEM;
}

/**
 * ebitmap_netlbl_import - Import a NetLabel category bitmap into an ebitmap
 * @ebmap: the ebitmap to import
 * @catmap: the NetLabel category bitmap
 *
 * Description:
 * Import a NetLabel category bitmap into a SELinux extensibile bitmap.
 * Returns zero on success, negative values on error.
 *
 */
int ebitmap_netlbl_import(struct ebitmap *ebmap,
                          struct netlbl_lsm_catmap *catmap)
{
        int rc;
        struct ebitmap_node *e_iter = NULL;
        struct ebitmap_node *e_prev = NULL;
        u32 offset = 0, idx;
        unsigned long bitmap;

        for (;;) {
                rc = netlbl_catmap_getlong(catmap, &offset, &bitmap);
                if (rc < 0)
                        goto netlbl_import_failure;
                if (offset == (u32)-1)
                        return 0;

                /* don't waste ebitmap space if the netlabel bitmap is empty */
                if (bitmap == 0) {
                        offset += EBITMAP_UNIT_SIZE;
                        continue;
                }

                if (e_iter == NULL ||
                    offset >= e_iter->startbit + EBITMAP_SIZE) {
                        e_prev = e_iter;
                        e_iter = kmem_cache_zalloc(ebitmap_node_cachep,
                                                   GFP_ATOMIC);
                        if (e_iter == NULL)
                                goto netlbl_import_failure;
                        e_iter->startbit = offset - (offset % EBITMAP_SIZE);
                        if (e_prev == NULL)
                                ebmap->node = e_iter;
                        else
                                e_prev->next = e_iter;
                        ebmap->highbit = e_iter->startbit + EBITMAP_SIZE;
                }

                /* offset will always be aligned to an unsigned long */
                idx = EBITMAP_NODE_INDEX(e_iter, offset);
                e_iter->maps[idx] = bitmap;

                /* next */
                offset += EBITMAP_UNIT_SIZE;
        }

        /* NOTE: we should never reach this return */
        return 0;

netlbl_import_failure:
        ebitmap_destroy(ebmap);
        return -ENOMEM;
}
#endif /* CONFIG_NETLABEL */

/*
 * Check to see if all the bits set in e2 are also set in e1. Optionally,
 * if last_e2bit is non-zero, the highest set bit in e2 cannot exceed
 * last_e2bit.
 */
int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2,
                     u32 last_e2bit)
{
        const struct ebitmap_node *n1, *n2;
        int i;

        if (e1->highbit < e2->highbit)
                return 0;

        n1 = e1->node;
        n2 = e2->node;

        while (n1 && n2 && (n1->startbit <= n2->startbit)) {
                if (n1->startbit < n2->startbit) {
                        n1 = n1->next;
                        continue;
                }
                for (i = EBITMAP_UNIT_NUMS - 1; (i >= 0) && !n2->maps[i];)
                        i--; /* Skip trailing NULL map entries */
                if (last_e2bit && (i >= 0)) {
                        u32 lastsetbit = n2->startbit + i * EBITMAP_UNIT_SIZE +
                                         __fls(n2->maps[i]);
                        if (lastsetbit > last_e2bit)
                                return 0;
                }

                while (i >= 0) {
                        if ((n1->maps[i] & n2->maps[i]) != n2->maps[i])
                                return 0;
                        i--;
                }

                n1 = n1->next;
                n2 = n2->next;
        }

        if (n2)
                return 0;

        return 1;
}

int ebitmap_get_bit(const struct ebitmap *e, u32 bit)
{
        const struct ebitmap_node *n;

        if (e->highbit < bit)
                return 0;

        n = e->node;
        while (n && (n->startbit <= bit)) {
                if ((n->startbit + EBITMAP_SIZE) > bit)
                        return ebitmap_node_get_bit(n, bit);
                n = n->next;
        }

        return 0;
}

int ebitmap_set_bit(struct ebitmap *e, u32 bit, int value)
{
        struct ebitmap_node *n, *prev, *new;

        prev = NULL;
        n = e->node;
        while (n && n->startbit <= bit) {
                if ((n->startbit + EBITMAP_SIZE) > bit) {
                        if (value) {
                                ebitmap_node_set_bit(n, bit);
                        } else {
                                u32 s;

                                ebitmap_node_clr_bit(n, bit);

                                s = find_first_bit(n->maps, EBITMAP_SIZE);
                                if (s < EBITMAP_SIZE)
                                        return 0;

                                /* drop this node from the bitmap */
                                if (!n->next) {
                                        /*
                                         * this was the highest map
                                         * within the bitmap
                                         */
                                        if (prev)
                                                e->highbit = prev->startbit +
                                                             EBITMAP_SIZE;
                                        else
                                                e->highbit = 0;
                                }
                                if (prev)
                                        prev->next = n->next;
                                else
                                        e->node = n->next;
                                kmem_cache_free(ebitmap_node_cachep, n);
                        }
                        return 0;
                }
                prev = n;
                n = n->next;
        }

        if (!value)
                return 0;

        new = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
        if (!new)
                return -ENOMEM;

        new->startbit = bit - (bit % EBITMAP_SIZE);
        ebitmap_node_set_bit(new, bit);

        if (!n)
                /* this node will be the highest map within the bitmap */
                e->highbit = new->startbit + EBITMAP_SIZE;

        if (prev) {
                new->next = prev->next;
                prev->next = new;
        } else {
                new->next = e->node;
                e->node = new;
        }

        return 0;
}

void ebitmap_destroy(struct ebitmap *e)
{
        struct ebitmap_node *n, *temp;

        if (!e)
                return;

        n = e->node;
        while (n) {
                temp = n;
                n = n->next;
                kmem_cache_free(ebitmap_node_cachep, temp);
        }

        e->highbit = 0;
        e->node = NULL;
}

int ebitmap_read(struct ebitmap *e, struct policy_file *fp)
{
        struct ebitmap_node *n = NULL;
        u32 mapunit, count, startbit, index, i;
        __le32 ebitmap_start;
        u64 map;
        __le64 mapbits;
        __le32 buf[3];
        int rc;

        ebitmap_init(e);

        rc = next_entry(buf, fp, sizeof buf);
        if (rc < 0)
                goto out;

        mapunit = le32_to_cpu(buf[0]);
        e->highbit = le32_to_cpu(buf[1]);
        count = le32_to_cpu(buf[2]);

        if (mapunit != BITS_PER_U64) {
                pr_err("SELinux: ebitmap: map size %u does not "
                       "match my size %u (high bit was %u)\n",
                       mapunit, BITS_PER_U64, e->highbit);
                goto bad;
        }

        /* round up e->highbit */
        e->highbit += EBITMAP_SIZE - 1;
        e->highbit -= (e->highbit % EBITMAP_SIZE);

        if (!e->highbit) {
                e->node = NULL;
                goto ok;
        }

        if (e->highbit && !count)
                goto bad;

        for (i = 0; i < count; i++) {
                rc = next_entry(&ebitmap_start, fp, sizeof(u32));
                if (rc < 0) {
                        pr_err("SELinux: ebitmap: truncated map\n");
                        goto bad;
                }
                startbit = le32_to_cpu(ebitmap_start);

                if (startbit & (mapunit - 1)) {
                        pr_err("SELinux: ebitmap start bit (%u) is "
                               "not a multiple of the map unit size (%u)\n",
                               startbit, mapunit);
                        goto bad;
                }
                if (startbit > e->highbit - mapunit) {
                        pr_err("SELinux: ebitmap start bit (%u) is "
                               "beyond the end of the bitmap (%u)\n",
                               startbit, (e->highbit - mapunit));
                        goto bad;
                }

                if (!n || startbit >= n->startbit + EBITMAP_SIZE) {
                        struct ebitmap_node *tmp;
                        tmp = kmem_cache_zalloc(ebitmap_node_cachep,
                                                GFP_KERNEL);
                        if (!tmp) {
                                pr_err("SELinux: ebitmap: out of memory\n");
                                rc = -ENOMEM;
                                goto bad;
                        }
                        /* round down */
                        tmp->startbit = startbit - (startbit % EBITMAP_SIZE);
                        if (n)
                                n->next = tmp;
                        else
                                e->node = tmp;
                        n = tmp;
                } else if (startbit <= n->startbit) {
                        pr_err("SELinux: ebitmap: start bit %u"
                               " comes after start bit %u\n",
                               startbit, n->startbit);
                        goto bad;
                }

                rc = next_entry(&mapbits, fp, sizeof(u64));
                if (rc < 0) {
                        pr_err("SELinux: ebitmap: truncated map\n");
                        goto bad;
                }
                map = le64_to_cpu(mapbits);
                if (!map) {
                        pr_err("SELinux: ebitmap: empty map\n");
                        goto bad;
                }

                index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE;
                while (map) {
                        n->maps[index++] = map & (-1UL);
                        map = EBITMAP_SHIFT_UNIT_SIZE(map);
                }
        }

        if (n && n->startbit + EBITMAP_SIZE != e->highbit) {
                pr_err("SELinux: ebitmap: high bit %u is not equal to the expected value %zu\n",
                       e->highbit, n->startbit + EBITMAP_SIZE);
                goto bad;
        }

ok:
        rc = 0;
out:
        return rc;
bad:
        if (!rc)
                rc = -EINVAL;
        ebitmap_destroy(e);
        goto out;
}

int ebitmap_write(const struct ebitmap *e, struct policy_file *fp)
{
        struct ebitmap_node *n;
        u32 bit, count, last_bit, last_startbit;
        __le32 buf[3];
        u64 map;
        int rc;

        buf[0] = cpu_to_le32(BITS_PER_U64);

        count = 0;
        last_bit = 0;
        last_startbit = U32_MAX;
        ebitmap_for_each_positive_bit(e, n, bit)
        {
                if (last_startbit == U32_MAX ||
                    rounddown(bit, BITS_PER_U64) > last_startbit) {
                        count++;
                        last_startbit = rounddown(bit, BITS_PER_U64);
                }
                last_bit = roundup(bit + 1, BITS_PER_U64);
        }
        buf[1] = cpu_to_le32(last_bit);
        buf[2] = cpu_to_le32(count);

        rc = put_entry(buf, sizeof(u32), 3, fp);
        if (rc)
                return rc;

        map = 0;
        last_startbit = U32_MAX;
        ebitmap_for_each_positive_bit(e, n, bit)
        {
                if (last_startbit == U32_MAX ||
                    rounddown(bit, BITS_PER_U64) > last_startbit) {
                        __le64 buf64[1];

                        /* this is the very first bit */
                        if (!map) {
                                last_startbit = rounddown(bit, BITS_PER_U64);
                                map = (u64)1 << (bit - last_startbit);
                                continue;
                        }

                        /* write the last node */
                        buf[0] = cpu_to_le32(last_startbit);
                        rc = put_entry(buf, sizeof(u32), 1, fp);
                        if (rc)
                                return rc;

                        buf64[0] = cpu_to_le64(map);
                        rc = put_entry(buf64, sizeof(u64), 1, fp);
                        if (rc)
                                return rc;

                        /* set up for the next node */
                        map = 0;
                        last_startbit = rounddown(bit, BITS_PER_U64);
                }
                map |= (u64)1 << (bit - last_startbit);
        }
        /* write the last node */
        if (map) {
                __le64 buf64[1];

                /* write the last node */
                buf[0] = cpu_to_le32(last_startbit);
                rc = put_entry(buf, sizeof(u32), 1, fp);
                if (rc)
                        return rc;

                buf64[0] = cpu_to_le64(map);
                rc = put_entry(buf64, sizeof(u64), 1, fp);
                if (rc)
                        return rc;
        }
        return 0;
}

u32 ebitmap_hash(const struct ebitmap *e, u32 hash)
{
        struct ebitmap_node *node;

        /* need to change hash even if ebitmap is empty */
        hash = jhash_1word(e->highbit, hash);
        for (node = e->node; node; node = node->next) {
                hash = jhash_1word(node->startbit, hash);
                hash = jhash(node->maps, sizeof(node->maps), hash);
        }
        return hash;
}

void __init ebitmap_cache_init(void)
{
        ebitmap_node_cachep = KMEM_CACHE(ebitmap_node, SLAB_PANIC);
}