[funini.com] -> [kei@sodan] -> Kernel Reading

root/security/selinux/ss/ebitmap.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ebitmap_cmp
  2. ebitmap_cpy
  3. ebitmap_netlbl_export
  4. ebitmap_netlbl_import
  5. ebitmap_contains
  6. ebitmap_get_bit
  7. ebitmap_set_bit
  8. ebitmap_destroy
  9. ebitmap_read

/*
 * Implementation of the extensible bitmap type.
 *
 * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
 */
/*
 * Updated: Hewlett-Packard <paul.moore@hp.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 <net/netlabel.h>
#include "ebitmap.h"
#include "policydb.h"

int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2)
{
        struct ebitmap_node *n1, *n2;

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

        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 0;

        return 1;
}

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

        ebitmap_init(dst);
        n = src->node;
        prev = NULL;
        while (n) {
                new = kzalloc(sizeof(*new), 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;
}

#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_secattr_catmap **catmap)
{
        struct ebitmap_node *e_iter = ebmap->node;
        struct netlbl_lsm_secattr_catmap *c_iter;
        u32 cmap_idx, cmap_sft;
        int i;

        /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64,
         * however, it is not always compatible with an array of unsigned long
         * in ebitmap_node.
         * In addition, you should pay attention the following implementation
         * assumes unsigned long has a width equal with or less than 64-bit.
         */

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

        c_iter = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
        if (c_iter == NULL)
                return -ENOMEM;
        *catmap = c_iter;
        c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1);

        while (e_iter) {
                for (i = 0; i < EBITMAP_UNIT_NUMS; i++) {
                        unsigned int delta, e_startbit, c_endbit;

                        e_startbit = e_iter->startbit + i * EBITMAP_UNIT_SIZE;
                        c_endbit = c_iter->startbit + NETLBL_CATMAP_SIZE;
                        if (e_startbit >= c_endbit) {
                                c_iter->next
                                  = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
                                if (c_iter->next == NULL)
                                        goto netlbl_export_failure;
                                c_iter = c_iter->next;
                                c_iter->startbit
                                  = e_startbit & ~(NETLBL_CATMAP_SIZE - 1);
                        }
                        delta = e_startbit - c_iter->startbit;
                        cmap_idx = delta / NETLBL_CATMAP_MAPSIZE;
                        cmap_sft = delta % NETLBL_CATMAP_MAPSIZE;
                        c_iter->bitmap[cmap_idx]
                                |= e_iter->maps[cmap_idx] << cmap_sft;
                }
                e_iter = e_iter->next;
        }

        return 0;

netlbl_export_failure:
        netlbl_secattr_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_secattr_catmap *catmap)
{
        struct ebitmap_node *e_iter = NULL;
        struct ebitmap_node *emap_prev = NULL;
        struct netlbl_lsm_secattr_catmap *c_iter = catmap;
        u32 c_idx, c_pos, e_idx, e_sft;

        /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64,
         * however, it is not always compatible with an array of unsigned long
         * in ebitmap_node.
         * In addition, you should pay attention the following implementation
         * assumes unsigned long has a width equal with or less than 64-bit.
         */

        do {
                for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) {
                        unsigned int delta;
                        u64 map = c_iter->bitmap[c_idx];

                        if (!map)
                                continue;

                        c_pos = c_iter->startbit
                                + c_idx * NETLBL_CATMAP_MAPSIZE;
                        if (!e_iter
                            || c_pos >= e_iter->startbit + EBITMAP_SIZE) {
                                e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC);
                                if (!e_iter)
                                        goto netlbl_import_failure;
                                e_iter->startbit
                                        = c_pos - (c_pos % EBITMAP_SIZE);
                                if (emap_prev == NULL)
                                        ebmap->node = e_iter;
                                else
                                        emap_prev->next = e_iter;
                                emap_prev = e_iter;
                        }
                        delta = c_pos - e_iter->startbit;
                        e_idx = delta / EBITMAP_UNIT_SIZE;
                        e_sft = delta % EBITMAP_UNIT_SIZE;
                        while (map) {
                                e_iter->maps[e_idx++] |= map & (-1UL);
                                map = EBITMAP_SHIFT_UNIT_SIZE(map);
                        }
                }
                c_iter = c_iter->next;
        } while (c_iter);
        if (e_iter != NULL)
                ebmap->highbit = e_iter->startbit + EBITMAP_SIZE;
        else
                ebitmap_destroy(ebmap);

        return 0;

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

int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
{
        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 = 0; i < EBITMAP_UNIT_NUMS; i++) {
                        if ((n1->maps[i] & n2->maps[i]) != n2->maps[i])
                                return 0;
                }

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

        if (n2)
                return 0;

        return 1;
}

int ebitmap_get_bit(struct ebitmap *e, unsigned long bit)
{
        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, unsigned long 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 {
                                unsigned int 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;
                                kfree(n);
                        }
                        return 0;
                }
                prev = n;
                n = n->next;
        }

        if (!value)
                return 0;

        new = kzalloc(sizeof(*new), 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;
                kfree(temp);
        }

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

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

        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 != sizeof(u64) * 8) {
                printk(KERN_ERR "SELinux: ebitmap: map size %u does not "
                       "match my size %Zd (high bit was %d)\n",
                       mapunit, sizeof(u64) * 8, 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;
        }

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

                if (startbit & (mapunit - 1)) {
                        printk(KERN_ERR "SELinux: ebitmap start bit (%d) is "
                               "not a multiple of the map unit size (%u)\n",
                               startbit, mapunit);
                        goto bad;
                }
                if (startbit > e->highbit - mapunit) {
                        printk(KERN_ERR "SELinux: ebitmap start bit (%d) 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 = kzalloc(sizeof(*tmp), GFP_KERNEL);
                        if (!tmp) {
                                printk(KERN_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) {
                        printk(KERN_ERR "SELinux: ebitmap: start bit %d"
                               " comes after start bit %d\n",
                               startbit, n->startbit);
                        goto bad;
                }

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

                index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE;
                while (map) {
                        n->maps[index++] = map & (-1UL);
                        map = EBITMAP_SHIFT_UNIT_SIZE(map);
                }
        }
ok:
        rc = 0;
out:
        return rc;
bad:
        if (!rc)
                rc = -EINVAL;
        ebitmap_destroy(e);
        goto out;
}

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading