* @tclass: target security class
  * @av: access vector
  */
-static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
+void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
 {
        const char **common_pts = NULL;
        u32 common_base = 0;
 
 
                if (sid == 0)
                        return -EINVAL;
-
-               /* Only allow single threaded processes to change context */
+               /*
+                * SELinux allows to change context in the following case only.
+                *  - Single threaded processes.
+                *  - Multi threaded processes intend to change its context into
+                *    more restricted domain (defined by TYPEBOUNDS statement).
+                */
                if (atomic_read(&p->mm->mm_users) != 1) {
                        struct task_struct *g, *t;
                        struct mm_struct *mm = p->mm;
                        do_each_thread(g, t) {
                                if (t->mm == mm && t != p) {
                                        read_unlock(&tasklist_lock);
-                                       return -EPERM;
+                                       error = security_bounded_transition(tsec->sid, sid);
+                                       if (!error)
+                                               goto boundary_ok;
+
+                                       return error;
                                }
                        } while_each_thread(g, t);
                        read_unlock(&tasklist_lock);
                }
+boundary_ok:
 
                /* Check permissions for the transition. */
                error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
 
 #include <linux/kdev_t.h>
 #include <linux/spinlock.h>
 #include <linux/init.h>
+#include <linux/audit.h>
 #include <linux/in6.h>
 #include <linux/path.h>
 #include <asm/system.h>
                     u32 events, u32 ssid, u32 tsid,
                     u16 tclass, u32 perms);
 
+/* Shows permission in human readable form */
+void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av);
+
 /* Exported to selinuxfs */
 int avc_get_hash_stats(char *page);
 extern unsigned int avc_cache_threshold;
 
 #define POLICYDB_VERSION_RANGETRANS    21
 #define POLICYDB_VERSION_POLCAP                22
 #define POLICYDB_VERSION_PERMISSIVE    23
+#define POLICYDB_VERSION_BOUNDARY      24
 
 /* Range of policy versions we understand*/
 #define POLICYDB_VERSION_MIN   POLICYDB_VERSION_BASE
 #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
 #define POLICYDB_VERSION_MAX   CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
 #else
-#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_PERMISSIVE
+#define POLICYDB_VERSION_MAX   POLICYDB_VERSION_BOUNDARY
 #endif
 
 #define CONTEXT_MNT    0x01
 extern int selinux_policycap_netpeer;
 extern int selinux_policycap_openperm;
 
+/*
+ * type_datum properties
+ * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY
+ */
+#define TYPEDATUM_PROPERTY_PRIMARY     0x0001
+#define TYPEDATUM_PROPERTY_ATTRIBUTE   0x0002
+
+/* limitation of boundary depth  */
+#define POLICYDB_BOUNDS_MAXDEPTH       4
+
 int security_load_policy(void *data, size_t len);
 
 int security_policycap_supported(unsigned int req_cap);
 int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
                                 u16 tclass);
 
+int security_bounded_transition(u32 oldsid, u32 newsid);
+
 int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
 
 int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
 
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/errno.h>
+#include <linux/audit.h>
 #include "security.h"
 
 #include "policydb.h"
                .version        = POLICYDB_VERSION_PERMISSIVE,
                .sym_num        = SYM_NUM,
                .ocon_num       = OCON_NUM,
-       }
+       },
+       {
+               .version        = POLICYDB_VERSION_BOUNDARY,
+               .sym_num        = SYM_NUM,
+               .ocon_num       = OCON_NUM,
+       },
 };
 
 static struct policydb_compat_info *policydb_lookup_compat(int version)
 
        role = datum;
        p = datap;
-       if (!role->value || role->value > p->p_roles.nprim)
+       if (!role->value
+           || role->value > p->p_roles.nprim
+           || role->bounds > p->p_roles.nprim)
                return -EINVAL;
        p->p_role_val_to_name[role->value - 1] = key;
        p->role_val_to_struct[role->value - 1] = role;
        p = datap;
 
        if (typdatum->primary) {
-               if (!typdatum->value || typdatum->value > p->p_types.nprim)
+               if (!typdatum->value
+                   || typdatum->value > p->p_types.nprim
+                   || typdatum->bounds > p->p_types.nprim)
                        return -EINVAL;
                p->p_type_val_to_name[typdatum->value - 1] = key;
+               p->type_val_to_struct[typdatum->value - 1] = typdatum;
        }
 
        return 0;
 
        usrdatum = datum;
        p = datap;
-       if (!usrdatum->value || usrdatum->value > p->p_users.nprim)
+       if (!usrdatum->value
+           || usrdatum->value > p->p_users.nprim
+           || usrdatum->bounds > p->p_users.nprim)
                return -EINVAL;
        p->p_user_val_to_name[usrdatum->value - 1] = key;
        p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
                goto out;
        }
 
+       p->type_val_to_struct =
+               kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)),
+                       GFP_KERNEL);
+       if (!p->type_val_to_struct) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
        if (cond_init_bool_indexes(p)) {
                rc = -ENOMEM;
                goto out;
        kfree(p->class_val_to_struct);
        kfree(p->role_val_to_struct);
        kfree(p->user_val_to_struct);
+       kfree(p->type_val_to_struct);
 
        avtab_destroy(&p->te_avtab);
 
 {
        char *key = NULL;
        struct role_datum *role;
-       int rc;
-       __le32 buf[2];
+       int rc, to_read = 2;
+       __le32 buf[3];
        u32 len;
 
        role = kzalloc(sizeof(*role), GFP_KERNEL);
                goto out;
        }
 
-       rc = next_entry(buf, fp, sizeof buf);
+       if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+               to_read = 3;
+
+       rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
        if (rc < 0)
                goto bad;
 
        len = le32_to_cpu(buf[0]);
        role->value = le32_to_cpu(buf[1]);
+       if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+               role->bounds = le32_to_cpu(buf[2]);
 
        key = kmalloc(len + 1, GFP_KERNEL);
        if (!key) {
 {
        char *key = NULL;
        struct type_datum *typdatum;
-       int rc;
-       __le32 buf[3];
+       int rc, to_read = 3;
+       __le32 buf[4];
        u32 len;
 
        typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL);
                return rc;
        }
 
-       rc = next_entry(buf, fp, sizeof buf);
+       if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+               to_read = 4;
+
+       rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
        if (rc < 0)
                goto bad;
 
        len = le32_to_cpu(buf[0]);
        typdatum->value = le32_to_cpu(buf[1]);
-       typdatum->primary = le32_to_cpu(buf[2]);
+       if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) {
+               u32 prop = le32_to_cpu(buf[2]);
+
+               if (prop & TYPEDATUM_PROPERTY_PRIMARY)
+                       typdatum->primary = 1;
+               if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE)
+                       typdatum->attribute = 1;
+
+               typdatum->bounds = le32_to_cpu(buf[3]);
+       } else {
+               typdatum->primary = le32_to_cpu(buf[2]);
+       }
 
        key = kmalloc(len + 1, GFP_KERNEL);
        if (!key) {
 {
        char *key = NULL;
        struct user_datum *usrdatum;
-       int rc;
-       __le32 buf[2];
+       int rc, to_read = 2;
+       __le32 buf[3];
        u32 len;
 
        usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL);
                goto out;
        }
 
-       rc = next_entry(buf, fp, sizeof buf);
+       if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+               to_read = 3;
+
+       rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
        if (rc < 0)
                goto bad;
 
        len = le32_to_cpu(buf[0]);
        usrdatum->value = le32_to_cpu(buf[1]);
+       if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+               usrdatum->bounds = le32_to_cpu(buf[2]);
 
        key = kmalloc(len + 1, GFP_KERNEL);
        if (!key) {
        cat_read,
 };
 
+static int user_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+       struct user_datum *upper, *user;
+       struct policydb *p = datap;
+       int depth = 0;
+
+       upper = user = datum;
+       while (upper->bounds) {
+               struct ebitmap_node *node;
+               unsigned long bit;
+
+               if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+                       printk(KERN_ERR "SELinux: user %s: "
+                              "too deep or looped boundary",
+                              (char *) key);
+                       return -EINVAL;
+               }
+
+               upper = p->user_val_to_struct[upper->bounds - 1];
+               ebitmap_for_each_positive_bit(&user->roles, node, bit) {
+                       if (ebitmap_get_bit(&upper->roles, bit))
+                               continue;
+
+                       printk(KERN_ERR
+                              "SELinux: boundary violated policy: "
+                              "user=%s role=%s bounds=%s\n",
+                              p->p_user_val_to_name[user->value - 1],
+                              p->p_role_val_to_name[bit],
+                              p->p_user_val_to_name[upper->value - 1]);
+
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int role_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+       struct role_datum *upper, *role;
+       struct policydb *p = datap;
+       int depth = 0;
+
+       upper = role = datum;
+       while (upper->bounds) {
+               struct ebitmap_node *node;
+               unsigned long bit;
+
+               if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+                       printk(KERN_ERR "SELinux: role %s: "
+                              "too deep or looped bounds\n",
+                              (char *) key);
+                       return -EINVAL;
+               }
+
+               upper = p->role_val_to_struct[upper->bounds - 1];
+               ebitmap_for_each_positive_bit(&role->types, node, bit) {
+                       if (ebitmap_get_bit(&upper->types, bit))
+                               continue;
+
+                       printk(KERN_ERR
+                              "SELinux: boundary violated policy: "
+                              "role=%s type=%s bounds=%s\n",
+                              p->p_role_val_to_name[role->value - 1],
+                              p->p_type_val_to_name[bit],
+                              p->p_role_val_to_name[upper->value - 1]);
+
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int type_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+       struct type_datum *upper, *type;
+       struct policydb *p = datap;
+       int depth = 0;
+
+       upper = type = datum;
+       while (upper->bounds) {
+               if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+                       printk(KERN_ERR "SELinux: type %s: "
+                              "too deep or looped boundary\n",
+                              (char *) key);
+                       return -EINVAL;
+               }
+
+               upper = p->type_val_to_struct[upper->bounds - 1];
+               if (upper->attribute) {
+                       printk(KERN_ERR "SELinux: type %s: "
+                              "bounded by attribute %s",
+                              (char *) key,
+                              p->p_type_val_to_name[upper->value - 1]);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int policydb_bounds_sanity_check(struct policydb *p)
+{
+       int rc;
+
+       if (p->policyvers < POLICYDB_VERSION_BOUNDARY)
+               return 0;
+
+       rc = hashtab_map(p->p_users.table,
+                        user_bounds_sanity_check, p);
+       if (rc)
+               return rc;
+
+       rc = hashtab_map(p->p_roles.table,
+                        role_bounds_sanity_check, p);
+       if (rc)
+               return rc;
+
+       rc = hashtab_map(p->p_types.table,
+                        type_bounds_sanity_check, p);
+       if (rc)
+               return rc;
+
+       return 0;
+}
+
 extern int ss_initialized;
 
 /*
                                goto bad;
        }
 
+       rc = policydb_bounds_sanity_check(p);
+       if (rc)
+               goto bad;
+
        rc = 0;
 out:
        return rc;
 
 /* Role attributes */
 struct role_datum {
        u32 value;                      /* internal role value */
+       u32 bounds;                     /* boundary of role */
        struct ebitmap dominates;       /* set of roles dominated by this role */
        struct ebitmap types;           /* set of authorized types for role */
 };
 /* Type attributes */
 struct type_datum {
        u32 value;              /* internal type value */
+       u32 bounds;             /* boundary of type */
        unsigned char primary;  /* primary name? */
+       unsigned char attribute;/* attribute ?*/
 };
 
 /* User attributes */
 struct user_datum {
        u32 value;                      /* internal user value */
+       u32 bounds;                     /* bounds of user */
        struct ebitmap roles;           /* set of authorized roles for user */
        struct mls_range range;         /* MLS range (min - max) for user */
        struct mls_level dfltlevel;     /* default login MLS level for user */
        struct class_datum **class_val_to_struct;
        struct role_datum **role_val_to_struct;
        struct user_datum **user_val_to_struct;
+       struct type_datum **type_val_to_struct;
 
        /* type enforcement access vectors and transitions */
        struct avtab te_avtab;
 
 static int context_struct_to_string(struct context *context, char **scontext,
                                    u32 *scontext_len);
 
+static int context_struct_compute_av(struct context *scontext,
+                                    struct context *tcontext,
+                                    u16 tclass,
+                                    u32 requested,
+                                    struct av_decision *avd);
 /*
  * Return the boolean value of a constraint expression
  * when it is applied to the specified source and target
        return s[0];
 }
 
+/*
+ * security_boundary_permission - drops violated permissions
+ * on boundary constraint.
+ */
+static void type_attribute_bounds_av(struct context *scontext,
+                                    struct context *tcontext,
+                                    u16 tclass,
+                                    u32 requested,
+                                    struct av_decision *avd)
+{
+       struct context lo_scontext;
+       struct context lo_tcontext;
+       struct av_decision lo_avd;
+       struct type_datum *source
+               = policydb.type_val_to_struct[scontext->type - 1];
+       struct type_datum *target
+               = policydb.type_val_to_struct[tcontext->type - 1];
+       u32 masked = 0;
+
+       if (source->bounds) {
+               memset(&lo_avd, 0, sizeof(lo_avd));
+
+               memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
+               lo_scontext.type = source->bounds;
+
+               context_struct_compute_av(&lo_scontext,
+                                         tcontext,
+                                         tclass,
+                                         requested,
+                                         &lo_avd);
+               if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+                       return;         /* no masked permission */
+               masked = ~lo_avd.allowed & avd->allowed;
+       }
+
+       if (target->bounds) {
+               memset(&lo_avd, 0, sizeof(lo_avd));
+
+               memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext));
+               lo_tcontext.type = target->bounds;
+
+               context_struct_compute_av(scontext,
+                                         &lo_tcontext,
+                                         tclass,
+                                         requested,
+                                         &lo_avd);
+               if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+                       return;         /* no masked permission */
+               masked = ~lo_avd.allowed & avd->allowed;
+       }
+
+       if (source->bounds && target->bounds) {
+               memset(&lo_avd, 0, sizeof(lo_avd));
+               /*
+                * lo_scontext and lo_tcontext are already
+                * set up.
+                */
+
+               context_struct_compute_av(&lo_scontext,
+                                         &lo_tcontext,
+                                         tclass,
+                                         requested,
+                                         &lo_avd);
+               if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+                       return;         /* no masked permission */
+               masked = ~lo_avd.allowed & avd->allowed;
+       }
+
+       if (masked) {
+               struct audit_buffer *ab;
+               char *stype_name
+                       = policydb.p_type_val_to_name[source->value - 1];
+               char *ttype_name
+                       = policydb.p_type_val_to_name[target->value - 1];
+               char *tclass_name
+                       = policydb.p_class_val_to_name[tclass - 1];
+
+               /* mask violated permissions */
+               avd->allowed &= ~masked;
+
+               /* notice to userspace via audit message */
+               ab = audit_log_start(current->audit_context,
+                                    GFP_ATOMIC, AUDIT_SELINUX_ERR);
+               if (!ab)
+                       return;
+
+               audit_log_format(ab, "av boundary violation: "
+                                "source=%s target=%s tclass=%s",
+                                stype_name, ttype_name, tclass_name);
+               avc_dump_av(ab, tclass, masked);
+               audit_log_end(ab);
+       }
+}
+
 /*
  * Compute access vectors based on a context structure pair for
  * the permissions in a particular class.
                                                        PROCESS__DYNTRANSITION);
        }
 
+       /*
+        * If the given source and target types have boundary
+        * constraint, lazy checks have to mask any violated
+        * permission and notice it to userspace via audit.
+        */
+       type_attribute_bounds_av(scontext, tcontext,
+                                tclass, requested, avd);
+
        return 0;
 
 inval_class:
        return rc;
 }
 
+/*
+ * security_bounded_transition - check whether the given
+ * transition is directed to bounded, or not.
+ * It returns 0, if @newsid is bounded by @oldsid.
+ * Otherwise, it returns error code.
+ *
+ * @oldsid : current security identifier
+ * @newsid : destinated security identifier
+ */
+int security_bounded_transition(u32 old_sid, u32 new_sid)
+{
+       struct context *old_context, *new_context;
+       struct type_datum *type;
+       int index;
+       int rc = -EINVAL;
+
+       read_lock(&policy_rwlock);
+
+       old_context = sidtab_search(&sidtab, old_sid);
+       if (!old_context) {
+               printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
+                      __func__, old_sid);
+               goto out;
+       }
+
+       new_context = sidtab_search(&sidtab, new_sid);
+       if (!new_context) {
+               printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
+                      __func__, new_sid);
+               goto out;
+       }
+
+       /* type/domain unchaned */
+       if (old_context->type == new_context->type) {
+               rc = 0;
+               goto out;
+       }
+
+       index = new_context->type;
+       while (true) {
+               type = policydb.type_val_to_struct[index - 1];
+               BUG_ON(!type);
+
+               /* not bounded anymore */
+               if (!type->bounds) {
+                       rc = -EPERM;
+                       break;
+               }
+
+               /* @newsid is bounded by @oldsid */
+               if (type->bounds == old_context->type) {
+                       rc = 0;
+                       break;
+               }
+               index = type->bounds;
+       }
+out:
+       read_unlock(&policy_rwlock);
+
+       return rc;
+}
+
+
 /**
  * security_compute_av - Compute access vector decisions.
  * @ssid: source security identifier
        *p++ = 0;
 
        typdatum = hashtab_search(pol->p_types.table, scontextp);
-       if (!typdatum)
+       if (!typdatum || typdatum->attribute)
                goto out;
 
        ctx->type = typdatum->value;