diff --git a/Android.mk b/Android.mk
index a41fc037a6e9df9c68ded2f5f43b6d432b24c94c..9b39a5993e81e8c20fb1ebdb7bff523ecdba215e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -351,7 +351,7 @@ $(service_contexts.tmp): $(all_svc_files) $(all_svcfiles_with_nl) $(built_nl)
 $(LOCAL_BUILT_MODULE): PRIVATE_SEPOLICY := $(built_sepolicy)
 $(LOCAL_BUILT_MODULE): $(service_contexts.tmp) $(built_sepolicy) $(HOST_OUT_EXECUTABLES)/checkfc $(ACP)
 	@mkdir -p $(dir $@)
-	$(hide) $(HOST_OUT_EXECUTABLES)/checkfc -p $(PRIVATE_SEPOLICY) $<
+	$(hide) $(HOST_OUT_EXECUTABLES)/checkfc -s $(PRIVATE_SEPOLICY) $<
 	$(hide) $(ACP) $< $@
 
 built_svc := $(LOCAL_BUILT_MODULE)
@@ -375,7 +375,7 @@ $(general_service_contexts.tmp): $(addprefix $(LOCAL_PATH)/, service_contexts)
 $(LOCAL_BUILT_MODULE): PRIVATE_SEPOLICY := $(built_general_sepolicy)
 $(LOCAL_BUILT_MODULE): $(general_service_contexts.tmp) $(built_general_sepolicy) $(HOST_OUT_EXECUTABLES)/checkfc $(ACP)
 	@mkdir -p $(dir $@)
-	$(hide) $(HOST_OUT_EXECUTABLES)/checkfc -p $(PRIVATE_SEPOLICY) $<
+	$(hide) $(HOST_OUT_EXECUTABLES)/checkfc -s $(PRIVATE_SEPOLICY) $<
 	$(hide) $(ACP) $< $@
 
 general_service_contexts.tmp :=
diff --git a/attributes b/attributes
index 3f4d5ef15589e7c15858ae8d4f9c4f25d31a7cfb..485b3e9a91ba9b2a14436ed534526e0ddd5473e3 100644
--- a/attributes
+++ b/attributes
@@ -3,6 +3,8 @@
 #
 
 # All types used for devices.
+# On change, update CHECK_FC_ASSERT_ATTRS
+# in tools/checkfc.c
 attribute dev_type;
 
 # All types used for processes.
@@ -19,6 +21,8 @@ attribute domain;
 attribute domain_deprecated;
 
 # All types used for filesystems.
+# On change, update CHECK_FC_ASSERT_ATTRS
+# definition in tools/checkfc.c.
 attribute fs_type;
 
 # All types used for context= mounts.
@@ -26,6 +30,8 @@ attribute contextmount_type;
 
 # All types used for files that can exist on a labeled fs.
 # Do not use for pseudo file types.
+# On change, update CHECK_FC_ASSERT_ATTRS
+# definition in tools/checkfc.c.
 attribute file_type;
 
 # All types used for domain entry points.
@@ -53,6 +59,8 @@ attribute netif_type;
 attribute port_type;
 
 # All types used for property service
+# On change, update CHECK_PC_ASSERT_ATTRS
+# definition in tools/checkfc.c.
 attribute property_type;
 
 # All properties defined in core SELinux policy. Should not be
@@ -69,6 +77,8 @@ attribute app_api_service;
 attribute system_api_service;
 
 # All types used for services managed by service_manager.
+# On change, update CHECK_SC_ASSERT_ATTRS
+# definition in tools/checkfc.c.
 attribute service_manager_type;
 
 # All domains that can override MLS restrictions.
diff --git a/tools/checkfc.c b/tools/checkfc.c
index 3b9a21698000ae1ccb7dc9aec46f53f749178969..602a05f762b17dd407d73fad0db6f1c0e0440946 100644
--- a/tools/checkfc.c
+++ b/tools/checkfc.c
@@ -1,35 +1,303 @@
 #include <getopt.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdbool.h>
+#include <string.h>
+#include <sepol/module.h>
+#include <sepol/policydb/policydb.h>
 #include <sepol/sepol.h>
 #include <selinux/selinux.h>
 #include <selinux/label.h>
 
-static int nerr;
+static const char * const CHECK_FC_ASSERT_ATTRS[] = { "fs_type", "dev_type", "file_type", NULL };
+static const char * const CHECK_PC_ASSERT_ATTRS[] = { "property_type", NULL };
+static const char * const CHECK_SC_ASSERT_ATTRS[] = { "service_manager_type", NULL };
+
+typedef enum filemode filemode;
+enum filemode {
+    filemode_file_contexts = 0,
+    filemode_property_contexts,
+    filemode_service_contexts
+};
+
+static struct {
+    /* policy */
+    struct {
+        union {
+            /* Union these so we don't have to cast */
+            sepol_policydb_t *sdb;
+            policydb_t *pdb;
+        };
+        sepol_policy_file_t *pf;
+        sepol_handle_t *handle;
+        FILE *file;
+#define SEHANDLE_CNT 2
+        struct selabel_handle *sehnd[SEHANDLE_CNT];
+    } sepolicy;
+
+    /* assertions */
+    struct {
+        const char * const *attrs; /* for the original set to print on error */
+        ebitmap_t set;             /* the ebitmap representation of the attrs */
+    } assert;
+
+} global_state;
+
+static const char * const *filemode_to_assert_attrs(filemode mode)
+{
+    switch (mode) {
+    case filemode_file_contexts:
+        return CHECK_FC_ASSERT_ATTRS;
+    case filemode_property_contexts:
+        return CHECK_PC_ASSERT_ATTRS;
+    case filemode_service_contexts:
+        return CHECK_SC_ASSERT_ATTRS;
+    }
+    /* die on invalid parameters */
+    fprintf(stderr, "Error: Invalid mode of operation: %d\n", mode);
+    exit(1);
+}
+
+static int get_attr_bit(policydb_t *policydb, const char *attr_name)
+{
+    struct type_datum *attr = hashtab_search(policydb->p_types.table, (char *)attr_name);
+    if (!attr) {
+        fprintf(stderr, "Error: \"%s\" is not defined in this policy.\n", attr_name);
+        return -1;
+    }
+
+    if (attr->flavor != TYPE_ATTRIB) {
+        fprintf(stderr, "Error: \"%s\" is not an attribute in this policy.\n", attr_name);
+        return -1;
+    }
+
+    return attr->s.value - 1;
+}
+
+static bool ebitmap_attribute_assertion_init(ebitmap_t *assertions, const char * const attributes[])
+{
+
+    while (*attributes) {
+
+        int bit_pos = get_attr_bit(global_state.sepolicy.pdb, *attributes);
+        if (bit_pos < 0) {
+            /* get_attr_bit() logs error */
+            return false;
+        }
+
+        int err = ebitmap_set_bit(assertions, bit_pos, 1);
+        if (err) {
+            fprintf(stderr, "Error: setting bit on assertion ebitmap!\n");
+            return false;
+        }
+        attributes++;
+    }
+    return true;
+}
+
+static bool is_type_of_attribute_set(policydb_t *policydb, const char *type_name,
+        ebitmap_t *attr_set)
+{
+    struct type_datum *type = hashtab_search(policydb->p_types.table, (char *)type_name);
+    if (!type) {
+        fprintf(stderr, "Error: \"%s\" is not defined in this policy.\n", type_name);
+        return false;
+    }
+
+    if (type->flavor != TYPE_TYPE) {
+        fprintf(stderr, "Error: \"%s\" is not a type in this policy.\n", type_name);
+        return false;
+    }
+
+    ebitmap_t dst;
+    ebitmap_init(&dst);
+
+    /* Take the intersection, if the set is empty, then its a failure */
+    int rc = ebitmap_and(&dst, attr_set, &policydb->type_attr_map[type->s.value - 1]);
+    if (rc) {
+        fprintf(stderr, "Error: Could not perform ebitmap_and: %d\n", rc);
+        exit(1);
+    }
+
+    bool res = (bool)ebitmap_length(&dst);
+
+    ebitmap_destroy(&dst);
+    return res;
+}
+
+static void dump_char_array(FILE *stream, const char * const *strings)
+{
+
+    const char * const *p = strings;
+
+    fprintf(stream, "\"");
+
+    while (*p) {
+        const char *s = *p++;
+        const char *fmt = *p ? "%s, " : "%s\"";
+        fprintf(stream, fmt, s);
+    }
+}
 
 static int validate(char **contextp)
 {
-  char *context = *contextp;
-  if (sepol_check_context(context) < 0) {
-    nerr++;
-    return -1;
-  }
-  return 0;
+    bool res;
+    char *context = *contextp;
+
+    sepol_context_t *ctx;
+    int rc = sepol_context_from_string(global_state.sepolicy.handle, context,
+            &ctx);
+    if (rc < 0) {
+        fprintf(stderr, "Error: Could not allocate context from string");
+        exit(1);
+    }
+
+    rc = sepol_context_check(global_state.sepolicy.handle,
+            global_state.sepolicy.sdb, ctx);
+    if (rc < 0) {
+        goto out;
+    }
+
+    const char *type_name = sepol_context_get_type(ctx);
+
+    uint32_t len = ebitmap_length(&global_state.assert.set);
+    if (len > 0) {
+        res = !is_type_of_attribute_set(global_state.sepolicy.pdb, type_name,
+                &global_state.assert.set);
+        if (res) {
+            fprintf(stderr, "Error: type \"%s\" is not of set: ", type_name);
+            dump_char_array(stderr, global_state.assert.attrs);
+            fprintf(stderr, "\n");
+            /* The calls above did not affect rc, so set error before going to out */
+            rc = -1;
+            goto out;
+        }
+    }
+    /* Success: Although it should be 0, we explicitly set rc to 0 for clarity */
+    rc = 0;
+
+ out:
+    sepol_context_free(ctx);
+    return rc;
 }
 
 static void usage(char *name) {
-    fprintf(stderr, "usage1:  %s [-p] sepolicy context_file\n\n", name);
-    fprintf(stderr, "Parses a context file and checks for syntax errors.\n");
-    fprintf(stderr, "The context_file is assumed to be a file_contexts file\n");
-    fprintf(stderr, "unless the -p option is used to indicate the property backend.\n\n");
-
-    fprintf(stderr, "usage2:  %s -c file_contexts1 file_contexts2\n\n", name);
-    fprintf(stderr, "Compares two file contexts files and reports one of subset, equal, superset, or incomparable.\n");
-    fprintf(stderr, "\n");
+    fprintf(stderr, "usage1:  %s [-p|-s] sepolicy context_file\n\n"
+        "Parses a context file and checks for syntax errors.\n"
+        "The context_file is assumed to be a file_contexts file\n"
+        "unless the -p or -s option is used to indicate the property or service backend respectively.\n\n"
+
+        "usage2:  %s -c file_contexts1 file_contexts2\n\n"
+        "Compares two file contexts files and reports one of subset, equal, superset, or incomparable.\n\n",
+        name, name);
     exit(1);
 }
 
+static void cleanup(void) {
+
+    if (global_state.sepolicy.file) {
+        fclose(global_state.sepolicy.file);
+    }
+
+    if (global_state.sepolicy.sdb) {
+        sepol_policydb_free(global_state.sepolicy.sdb);
+    }
+
+    if (global_state.sepolicy.pf) {
+        sepol_policy_file_free(global_state.sepolicy.pf);
+    }
+
+    if (global_state.sepolicy.handle) {
+        sepol_handle_destroy(global_state.sepolicy.handle);
+    }
+
+    ebitmap_destroy(&global_state.assert.set);
+
+    int i;
+    for (i = 0; i < SEHANDLE_CNT; i++) {
+        struct selabel_handle *sehnd = global_state.sepolicy.sehnd[i];
+        if (sehnd) {
+            selabel_close(sehnd);
+        }
+    }
+}
+
+static void do_compare_and_die_on_error(struct selinux_opt opts[], unsigned int backend, char *paths[])
+{
+    enum selabel_cmp_result result;
+     char *result_str[] = { "subset", "equal", "superset", "incomparable" };
+     int i;
+
+     opts[0].value = NULL; /* not validating against a policy when comparing */
+
+     for (i = 0; i < SEHANDLE_CNT; i++) {
+         opts[1].value = paths[i];
+         global_state.sepolicy.sehnd[i] = selabel_open(backend, opts, 2);
+         if (!global_state.sepolicy.sehnd[i]) {
+             fprintf(stderr, "Error: could not load context file from %s\n", paths[i]);
+             exit(1);
+         }
+     }
+
+     result = selabel_cmp(global_state.sepolicy.sehnd[0], global_state.sepolicy.sehnd[1]);
+     printf("%s\n", result_str[result]);
+}
+
+static void do_fc_check_and_die_on_error(struct selinux_opt opts[], unsigned int backend, filemode mode,
+        const char *sepolicy_file, const char *context_file)
+{
+    global_state.sepolicy.file = fopen(sepolicy_file, "r");
+    if (!global_state.sepolicy.file) {
+      perror("Error: could not open policy file");
+      exit(1);
+    }
+
+    global_state.sepolicy.handle = sepol_handle_create();
+    if (!global_state.sepolicy.handle) {
+        fprintf(stderr, "Error: could not create policy handle: %s\n", strerror(errno));
+        exit(1);
+    }
+
+    if (sepol_policy_file_create(&global_state.sepolicy.pf) < 0) {
+      perror("Error: could not create policy handle");
+      exit(1);
+    }
+
+    sepol_policy_file_set_fp(global_state.sepolicy.pf, global_state.sepolicy.file);
+    sepol_policy_file_set_handle(global_state.sepolicy.pf, global_state.sepolicy.handle);
+
+    int rc = sepol_policydb_create(&global_state.sepolicy.sdb);
+    if (rc < 0) {
+      perror("Error: could not create policy db");
+      exit(1);
+    }
+
+    rc = sepol_policydb_read(global_state.sepolicy.sdb, global_state.sepolicy.pf);
+    if (rc < 0) {
+      perror("Error: could not read file into policy db");
+      exit(1);
+    }
+
+    global_state.assert.attrs = filemode_to_assert_attrs(mode);
+
+    bool ret = ebitmap_attribute_assertion_init(&global_state.assert.set, global_state.assert.attrs);
+    if (!ret) {
+        /* error messages logged by ebitmap_attribute_assertion_init() */
+        exit(1);
+    }
+
+    selinux_set_callback(SELINUX_CB_VALIDATE,
+                         (union selinux_callback)&validate);
+
+    opts[1].value = context_file;
+
+    global_state.sepolicy.sehnd[0] = selabel_open(backend, opts, 2);
+    if (!global_state.sepolicy.sehnd[0]) {
+      fprintf(stderr, "Error: could not load context file from %s\n", context_file);
+      exit(1);
+    }
+}
+
 int main(int argc, char **argv)
 {
   struct selinux_opt opts[] = {
@@ -40,17 +308,22 @@ int main(int argc, char **argv)
   // Default backend unless changed by input argument.
   unsigned int backend = SELABEL_CTX_FILE;
 
-  FILE *fp;
   bool compare = false;
-  struct selabel_handle *sehnd[2];
   char c;
 
-  while ((c = getopt(argc, argv, "cph")) != -1) {
+  filemode mode = filemode_file_contexts;
+
+  while ((c = getopt(argc, argv, "cps")) != -1) {
     switch (c) {
       case 'c':
         compare = true;
         break;
       case 'p':
+        mode = filemode_property_contexts;
+        backend = SELABEL_CTX_ANDROID_PROP;
+        break;
+      case 's':
+        mode = filemode_service_contexts;
         backend = SELABEL_CTX_ANDROID_PROP;
         break;
       case 'h':
@@ -69,57 +342,16 @@ int main(int argc, char **argv)
     usage(argv[0]);
   }
 
-  if (compare) {
-    enum selabel_cmp_result result;
-    char *result_str[] = { "subset", "equal", "superset", "incomparable" };
-    int i;
-
-    opts[0].value = NULL; /* not validating against a policy when comparing */
-
-    for (i = 0; i < 2; i++) {
-        opts[1].value = argv[index+i];
-        sehnd[i] = selabel_open(backend, opts, 2);
-        if (!sehnd[i]) {
-            fprintf(stderr, "Error loading context file from %s\n", argv[index+i]);
-            exit(1);
-        }
-    }
-
-    result = selabel_cmp(sehnd[0], sehnd[1]);
-    for (i = 0; i < 2; i++)
-        selabel_close(sehnd[i]);
-    printf("%s\n", result_str[result]);
-    exit(0);
-  }
-
-  // remaining args are sepolicy file and context file
-  char *sepolicyFile = argv[index];
-  char *contextFile = argv[index + 1];
+  atexit(cleanup);
 
-  fp = fopen(sepolicyFile, "r");
-  if (!fp) {
-    perror(sepolicyFile);
-    exit(1);
-  }
-  if (sepol_set_policydb_from_file(fp) < 0) {
-    fprintf(stderr, "Error loading policy from %s\n", sepolicyFile);
-    exit(1);
-  }
-
-  selinux_set_callback(SELINUX_CB_VALIDATE,
-                       (union selinux_callback)&validate);
-
-  opts[1].value = contextFile;
+  if (compare) {
+      do_compare_and_die_on_error(opts, backend, &(argv[index]));
+  } else {
+      /* remaining args are sepolicy file and context file  */
+      char *sepolicy_file = argv[index];
+      char *context_file = argv[index + 1];
 
-  sehnd[0] = selabel_open(backend, opts, 2);
-  if (!sehnd[0]) {
-    fprintf(stderr, "Error loading context file from %s\n", contextFile);
-    exit(1);
+      do_fc_check_and_die_on_error(opts, backend, mode, sepolicy_file, context_file);
   }
-  if (nerr) {
-    fprintf(stderr, "Invalid context file found in %s\n", contextFile);
-    exit(1);
-  }
-
   exit(0);
 }