Snap for 8512216 from 7ea0a4df6cb4f6c1afb9922c1c6f8a965774b2c2 to tm-frc-scheduling-release

Change-Id: I8ef1624259ffe9f59e331da3eb1fe39b51e83f6d
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 5d3177d..af20484 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -13,7 +13,7 @@
 
     # Install dependencies
     - run: sudo apt-get update -qq
-    - run: sudo apt-get install -qq bison clang clang-tools flex gawk gettext libaudit-dev libcap-dev libcap-ng-dev libcunit1-dev libdbus-glib-1-dev libpcre3-dev python3-dev python-dev ruby-dev swig xmlto
+    - run: sudo apt-get install -qq bison clang clang-tools flex gawk gettext libaudit-dev libcap-dev libcap-ng-dev libcunit1-dev libdbus-glib-1-dev libpcre2-dev python3-dev python-dev ruby-dev swig xmlto
 
     - run:
         name: Setup environment variables
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index 5c2233a..92523db 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -28,8 +28,9 @@
         uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
         with:
           oss-fuzz-project-name: 'selinux'
-          fuzz-seconds: 180
+          fuzz-seconds: 600
           dry-run: false
+          report-unreproducible-crashes: true
           sanitizer: ${{ matrix.sanitizer }}
       - name: Upload Crash
         uses: actions/upload-artifact@v1
diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml
index ef4be8a..8b7cb72 100644
--- a/.github/workflows/run_tests.yml
+++ b/.github/workflows/run_tests.yml
@@ -29,6 +29,9 @@
             python-ruby-version: {python: 3.9, ruby: 2.7, other: linker-bfd}
           - compiler: clang
             python-ruby-version: {python: 3.9, ruby: 2.7, other: linker-gold}
+        include:
+          - compiler: gcc
+            python-ruby-version: {python: 3.9, ruby: 2.7, other: sanitizers}
 
     steps:
     - uses: actions/checkout@v2
@@ -57,7 +60,7 @@
             libcap-ng-dev \
             libcunit1-dev \
             libdbus-glib-1-dev \
-            libpcre3-dev \
+            libpcre2-dev \
             python3-dev \
             python-dev \
             ruby-dev \
@@ -88,6 +91,11 @@
         elif [ "${{ matrix.python-ruby-version.other }}" = "test-debug" ] ; then
             # Test hat debug build works fine
             EXPLICIT_MAKE_VARS="DEBUG=1"
+        elif [ "${{ matrix.python-ruby-version.other }}" = "sanitizers" ] ; then
+            sanitizers='-fsanitize=address,undefined'
+            EXPLICIT_MAKE_VARS="CFLAGS='-g -I$DESTDIR/usr/include $sanitizers' LDFLAGS='-L$DESTDIR/usr/lib $sanitizers' LDLIBS= CPPFLAGS= OPT_SUBDIRS="
+            echo "ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1" >> $GITHUB_ENV
+            echo "UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1" >> $GITHUB_ENV
         else
             EXPLICIT_MAKE_VARS=
         fi
@@ -139,18 +147,18 @@
     - name: Run tests
       run: |
         echo "::group::make install"
-        make -j$(nproc) install $EXPLICIT_MAKE_VARS -k
+        eval make -j$(nproc) install $EXPLICIT_MAKE_VARS -k
         echo "::endgroup::"
         echo "::group::make install-pywrap"
-        make -j$(nproc) install-pywrap $EXPLICIT_MAKE_VARS -k
+        eval make -j$(nproc) install-pywrap $EXPLICIT_MAKE_VARS -k
         echo "::endgroup::"
         echo "::group::make install-rubywrap"
-        make -j$(nproc) install-rubywrap $EXPLICIT_MAKE_VARS -k
+        eval make -j$(nproc) install-rubywrap $EXPLICIT_MAKE_VARS -k
         echo "::endgroup::"
 
         # Now that everything is installed, run "make all" to build everything which may have not been built
         echo "::group::make all"
-        make -j$(nproc) all $EXPLICIT_MAKE_VARS -k
+        eval make -j$(nproc) all $EXPLICIT_MAKE_VARS -k
         echo "::endgroup::"
 
         # Set up environment variables for the tests and show variables (to help debugging issues)
@@ -164,19 +172,21 @@
 
         # Run tests
         echo "::group::make test"
-        make test $EXPLICIT_MAKE_VARS
+        eval make test $EXPLICIT_MAKE_VARS
         echo "::endgroup::"
 
-        # Test Python and Ruby wrappers
-        echo "::group::Test Python and Ruby wrappers"
-        $PYTHON -c 'import selinux;import selinux.audit2why;import semanage;print(selinux.is_selinux_enabled())'
-        $RUBY -e 'require "selinux";require "semanage";puts Selinux::is_selinux_enabled()'
-        echo "::endgroup::"
+        if [ "${{ matrix.python-ruby-version.other }}" != "sanitizers" ] ; then
+            # Test Python and Ruby wrappers
+            echo "::group::Test Python and Ruby wrappers"
+            $PYTHON -c 'import selinux;import selinux.audit2why;import semanage;print(selinux.is_selinux_enabled())'
+            $RUBY -e 'require "selinux";require "semanage";puts Selinux::is_selinux_enabled()'
+            echo "::endgroup::"
 
-        # Run Python linter, but not on the downloaded refpolicy
-        echo "::group::scripts/run-flake8"
-        ./scripts/run-flake8
-        echo "::endgroup::"
+            # Run Python linter, but not on the downloaded refpolicy
+            echo "::group::scripts/run-flake8"
+            ./scripts/run-flake8
+            echo "::endgroup::"
+        fi
 
         echo "::group::Test .gitignore and make clean distclean"
         # Remove every installed files
@@ -184,6 +194,6 @@
         # Test that "git status" looks clean, or print a clear error message
         git status --short | sed -n 's/^??/error: missing .gitignore entry for/p' | (! grep '^')
         # Clean up everything and show which file needs to be added to "make clean"
-        make clean distclean $EXPLICIT_MAKE_VARS
+        eval make clean distclean $EXPLICIT_MAKE_VARS
         git ls-files --ignored --others --exclude-standard | sed 's/^/error: "make clean distclean" did not remove /' | (! grep '^')
         echo "::endgroup::"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a3517cb..7c548e5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -26,7 +26,7 @@
 There are a number of dependencies required to build the userspace
 tools/libraries. On a Fedora system you can install them with yum:
 
-    # yum install audit-libs-devel bison bzip2-devel dbus-devel dbus-glib-devel flex flex-devel flex-static glib2-devel libcap-devel libcap-ng-devel pam-devel pcre-devel python-devel setools-devel swig ustr-devel
+    # yum install audit-libs-devel bison bzip2-devel dbus-devel dbus-glib-devel flex flex-devel flex-static glib2-devel libcap-devel libcap-ng-devel pam-devel pcre2-devel python-devel setools-devel swig ustr-devel
 
 The tools and libraries can be built and installed under a private directory from the top level with make, e.g.
 
diff --git a/Makefile b/Makefile
index 298cd2b..215e313 100644
--- a/Makefile
+++ b/Makefile
@@ -9,8 +9,12 @@
 	export LDFLAGS = -g
 else
 	export CFLAGS ?= -O2 -Werror -Wall -Wextra \
+		-Wfloat-equal \
+		-Wformat=2 \
+		-Winit-self \
 		-Wmissing-format-attribute \
 		-Wmissing-noreturn \
+		-Wnull-dereference \
 		-Wpointer-arith \
 		-Wshadow \
 		-Wstrict-prototypes \
diff --git a/README.md b/README.md
index e1c2fe6..74b0a0c 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@
     libcap-devel \
     libcap-ng-devel \
     pam-devel \
-    pcre-devel \
+    pcre2-devel \
     xmlto
 
 # For Python and Ruby bindings
@@ -78,7 +78,7 @@
     libcap-ng-dev \
     libcunit1-dev \
     libglib2.0-dev \
-    libpcre3-dev \
+    libpcre2-dev \
     pkgconf \
     python3 \
     python3-distutils \
diff --git a/checkpolicy/module_compiler.c b/checkpolicy/module_compiler.c
index 5f5b0b1..129650f 100644
--- a/checkpolicy/module_compiler.c
+++ b/checkpolicy/module_compiler.c
@@ -99,6 +99,7 @@
 				yyerror("no module name");
 				return -1;
 			}
+			free(policydbp->name);
 			policydbp->name = id;
 			if ((policydbp->version =
 			     queue_remove(id_queue)) == NULL) {
diff --git a/checkpolicy/parse_util.c b/checkpolicy/parse_util.c
index 8c1f393..f2d1e04 100644
--- a/checkpolicy/parse_util.c
+++ b/checkpolicy/parse_util.c
@@ -47,6 +47,7 @@
 	}
 
 	policydbp = p;
+	policydbp->name = strdup(file);
 	mlspol = p->mls;
 
 	init_parser(1);
diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
index d3eb611..16b7834 100644
--- a/checkpolicy/policy_define.c
+++ b/checkpolicy/policy_define.c
@@ -3477,6 +3477,8 @@
 	return NULL;
 }
 
+#define PERMISSION_MASK(nprim) ((nprim) == PERM_SYMTAB_SIZE ? (~UINT32_C(0)) : ((UINT32_C(1) << (nprim)) - 1))
+
 int define_constraint(constraint_expr_t * expr)
 {
 	struct constraint_node *node;
@@ -3590,6 +3592,22 @@
 			cladatum = policydbp->class_val_to_struct[i];
 			node = cladatum->constraints;
 
+			if (strcmp(id, "*") == 0) {
+				node->permissions = PERMISSION_MASK(cladatum->permissions.nprim);
+				continue;
+			}
+
+			if (strcmp(id, "~") == 0) {
+				node->permissions = ~node->permissions & PERMISSION_MASK(cladatum->permissions.nprim);
+				if (node->permissions == 0) {
+					yywarn("omitting constraint with no permission set");
+					cladatum->constraints = node->next;
+					constraint_expr_destroy(node->expr);
+					free(node);
+				}
+				continue;
+			}
+
 			perdatum =
 			    (perm_datum_t *) hashtab_search(cladatum->
 							    permissions.
@@ -5290,6 +5308,14 @@
 		goto out;
 	}
 
+	if (mask.s_addr != 0 && ((~mask.s_addr + 1) & ~mask.s_addr) != 0) {
+		yywarn("ipv4 mask is not contiguous");
+	}
+
+	if ((~mask.s_addr & addr.s_addr) != 0) {
+		yywarn("host bits in ipv4 address set");
+	}
+
 	newc = malloc(sizeof(ocontext_t));
 	if (!newc) {
 		yyerror("out of memory");
@@ -5325,6 +5351,40 @@
 	return rc;
 }
 
+static int ipv6_is_mask_contiguous(const struct in6_addr *mask)
+{
+	int filled = 1;
+	unsigned i;
+
+	for (i = 0; i < 16; i++) {
+		if ((((~mask->s6_addr[i] & 0xFF) + 1) & (~mask->s6_addr[i] & 0xFF)) != 0) {
+			return 0;
+		}
+		if (!filled && mask->s6_addr[i] != 0) {
+			return 0;
+		}
+
+		if (filled && mask->s6_addr[i] != 0xFF) {
+			filled = 0;
+		}
+	}
+
+	return 1;
+}
+
+static int ipv6_has_host_bits_set(const struct in6_addr *addr, const struct in6_addr *mask)
+{
+	unsigned i;
+
+	for (i = 0; i < 16; i++) {
+		if ((addr->s6_addr[i] & ~mask->s6_addr[i]) != 0) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
 int define_ipv6_node_context(void)
 {
 	char *id;
@@ -5376,6 +5436,14 @@
 		goto out;
 	}
 
+	if (!ipv6_is_mask_contiguous(&mask)) {
+		yywarn("ipv6 mask is not contiguous");
+	}
+
+	if (ipv6_has_host_bits_set(&addr, &mask)) {
+		yywarn("host bits in ipv6 address set");
+	}
+
 	newc = malloc(sizeof(ocontext_t));
 	if (!newc) {
 		yyerror("out of memory");
diff --git a/checkpolicy/policy_scan.l b/checkpolicy/policy_scan.l
index 129a8a2..9fefea7 100644
--- a/checkpolicy/policy_scan.l
+++ b/checkpolicy/policy_scan.l
@@ -60,7 +60,14 @@
 
 %%
 \n.*				{
+#if defined(__GNUC__) && __GNUC__ >= 8
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-truncation"
+#endif
 				  strncpy(linebuf[lno], yytext+1, 255);
+#if defined(__GNUC__) && __GNUC__ >= 8
+#pragma GCC diagnostic pop
+#endif
 				  linebuf[lno][254] = 0;
 				  lno = 1 - lno;
 				  policydb_lineno++;
@@ -308,11 +315,11 @@
 int yyerror(const char *msg)
 {
 	if (source_file[0])
-		fprintf(stderr, "%s:%ld:",
+		fprintf(stderr, "%s:%lu:",
 			source_file, source_lineno);
 	else
 		fprintf(stderr, "(unknown source)::");
-	fprintf(stderr, "ERROR '%s' at token '%s' on line %ld:\n%s\n%s\n",
+	fprintf(stderr, "ERROR '%s' at token '%s' on line %lu:\n%s\n%s\n",
 			msg,
 			yytext,
 			policydb_lineno,
@@ -327,11 +334,11 @@
 		return yyerror(msg);
 
 	if (source_file[0])
-		fprintf(stderr, "%s:%ld:",
+		fprintf(stderr, "%s:%lu:",
 			source_file, source_lineno);
 	else
 		fprintf(stderr, "(unknown source)::");
-	fprintf(stderr, "WARNING '%s' at token '%s' on line %ld:\n%s\n%s\n",
+	fprintf(stderr, "WARNING '%s' at token '%s' on line %lu:\n%s\n%s\n",
 			msg,
 			yytext,
 			policydb_lineno,
diff --git a/libselinux/Android.bp b/libselinux/Android.bp
index 6275b41..fab05ba 100644
--- a/libselinux/Android.bp
+++ b/libselinux/Android.bp
@@ -150,9 +150,8 @@
     ],
 
     target: {
-        linux_glibc: {
+        host_linux: {
             srcs: [
-                "src/android/android_host.c",
                 "src/avc.c",
                 "src/avc_internal.c",
                 "src/avc_sidtab.c",
@@ -162,49 +161,29 @@
                 "src/context.c",
                 "src/deny_unknown.c",
                 "src/enabled.c",
-                "src/fgetfilecon.c",
                 "src/getenforce.c",
                 "src/getfilecon.c",
                 "src/get_initial_context.c",
                 "src/init.c",
-                "src/lgetfilecon.c",
                 "src/load_policy.c",
-                "src/lsetfilecon.c",
                 "src/mapping.c",
                 "src/procattr.c",
                 "src/reject_unknown.c",
                 "src/sestatus.c",
-                "src/setenforce.c",
                 "src/setexecfilecon.c",
-                "src/setfilecon.c",
                 "src/stringrep.c",
             ],
         },
+        linux_glibc: {
+            srcs: [
+                "src/fgetfilecon.c",
+                "src/lgetfilecon.c",
+                "src/lsetfilecon.c",
+                "src/setfilecon.c",
+            ],
+        },
         linux_bionic: {
             enabled: true,
-            srcs: [
-                "src/android/android_host.c",
-                "src/avc.c",
-                "src/avc_internal.c",
-                "src/avc_sidtab.c",
-                "src/compute_av.c",
-                "src/compute_create.c",
-                "src/compute_member.c",
-                "src/context.c",
-                "src/deny_unknown.c",
-                "src/enabled.c",
-                "src/getenforce.c",
-                "src/getfilecon.c",
-                "src/get_initial_context.c",
-                "src/init.c",
-                "src/load_policy.c",
-                "src/mapping.c",
-                "src/procattr.c",
-                "src/reject_unknown.c",
-                "src/sestatus.c",
-                "src/setexecfilecon.c",
-                "src/stringrep.c",
-            ],
         },
 
         android: {
diff --git a/libselinux/Makefile b/libselinux/Makefile
index 439bc6a..6d9e273 100644
--- a/libselinux/Makefile
+++ b/libselinux/Makefile
@@ -23,7 +23,7 @@
 endif
 export DISABLE_SETRANS DISABLE_RPM DISABLE_FLAGS ANDROID_HOST DISABLE_X11 LABEL_BACKEND_ANDROID
 
-USE_PCRE2 ?= n
+USE_PCRE2 ?= y
 ifeq ($(USE_PCRE2),y)
 	PCRE_MODULE := libpcre2-8
 	PCRE_CFLAGS := -DUSE_PCRE2 -DPCRE2_CODE_UNIT_WIDTH=8
diff --git a/libselinux/include/selinux/restorecon.h b/libselinux/include/selinux/restorecon.h
index 466de39..1821a3d 100644
--- a/libselinux/include/selinux/restorecon.h
+++ b/libselinux/include/selinux/restorecon.h
@@ -2,6 +2,7 @@
 #define _RESTORECON_H_
 
 #include <sys/types.h>
+#include <stddef.h>
 #include <stdarg.h>
 
 #ifdef __cplusplus
@@ -23,6 +24,19 @@
  */
 extern int selinux_restorecon(const char *pathname,
 				    unsigned int restorecon_flags);
+/**
+ * selinux_restorecon_parallel - Relabel files, optionally use more threads.
+ * @pathname: specifies file/directory to relabel.
+ * @restorecon_flags: specifies the actions to be performed when relabeling.
+ * @nthreads: specifies the number of threads to use (0 = use number of CPUs
+ *            currently online)
+ *
+ * Same as selinux_restorecon(3), but allows to use multiple threads to do
+ * the work.
+ */
+extern int selinux_restorecon_parallel(const char *pathname,
+				       unsigned int restorecon_flags,
+				       size_t nthreads);
 /*
  * restorecon_flags options
  */
diff --git a/libselinux/man/man3/selinux_restorecon.3 b/libselinux/man/man3/selinux_restorecon.3
index ad63740..334d293 100644
--- a/libselinux/man/man3/selinux_restorecon.3
+++ b/libselinux/man/man3/selinux_restorecon.3
@@ -11,6 +11,14 @@
 .br
 .BI "unsigned int " restorecon_flags ");"
 .in
+.sp
+.BI "int selinux_restorecon_parallel(const char *" pathname ,
+.in +\w'int selinux_restorecon_parallel('u
+.br
+.BI "unsigned int " restorecon_flags ","
+.br
+.BI "size_t " nthreads ");"
+.in
 .
 .SH "DESCRIPTION"
 .BR selinux_restorecon ()
@@ -187,6 +195,27 @@
 .B SELINUX_RESTORECON_IGNORE_MOUNTS
 flag has been set.
 .RE
+.sp
+.BR selinux_restorecon_parallel()
+is similar to
+.BR selinux_restorecon (3),
+but accepts another parameter that allows to run relabeling over multiple
+threads:
+.sp
+.RS
+.IR nthreads
+specifies the number of threads to use during relabeling. When set to 1,
+the behavior is the same as calling
+.BR selinux_restorecon (3).
+When set to 0, the function will try to use as many threads as there are
+online CPU cores. When set to any other number, the function will try to use
+the given number of threads.
+.sp
+Note that to use the parallel relabeling capability, the calling process
+must be linked with the
+.B libpthread
+library (either at compile time or dynamically at run time). Otherwise the
+function will print a warning and fall back to the single threaded mode.
 .
 .SH "RETURN VALUE"
 On success, zero is returned.  On error, \-1 is returned and
diff --git a/libselinux/man/man3/selinux_restorecon_parallel.3 b/libselinux/man/man3/selinux_restorecon_parallel.3
new file mode 100644
index 0000000..092d841
--- /dev/null
+++ b/libselinux/man/man3/selinux_restorecon_parallel.3
@@ -0,0 +1 @@
+.so man3/selinux_restorecon.3
diff --git a/libselinux/src/Makefile b/libselinux/src/Makefile
index 52c40f0..04bf4f2 100644
--- a/libselinux/src/Makefile
+++ b/libselinux/src/Makefile
@@ -98,7 +98,6 @@
 LD_SONAME_FLAGS=-install_name,$(LIBSO)
 endif
 
-PCRE_LDLIBS ?= -lpcre
 # override with -lfts when building on Musl libc to use fts-standalone
 FTS_LDLIBS ?=
 
diff --git a/libselinux/src/android/android_host.c b/libselinux/src/android/android_host.c
deleted file mode 100644
index 8b55aa7..0000000
--- a/libselinux/src/android/android_host.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <stdlib.h>
-
-// HACK: placeholder for a library the python bindings expect.
-// Delete after b/33170640 is fixed.
-const char *selinux_openssh_contexts_path(void)
-{
-  abort();
-}
diff --git a/libselinux/src/android/android_platform.c b/libselinux/src/android/android_platform.c
index 2516c09..05c923b 100644
--- a/libselinux/src/android/android_platform.c
+++ b/libselinux/src/android/android_platform.c
@@ -806,9 +806,12 @@
 
 		username = pwd->pw_name;
 
-	} else if (appid < AID_ISOLATED_START) {
+	} else if (appid < AID_SDK_SANDBOX_PROCESS_START) {
 		username = "_app";
 		appid -= AID_APP;
+	} else if (appid < AID_ISOLATED_START) {
+		username = "_sdksandbox";
+		appid -= AID_SDK_SANDBOX_PROCESS_START;
 	} else {
 		username = "_isolated";
 		appid -= AID_ISOLATED_START;
@@ -1119,20 +1122,49 @@
  * credentials are presented (filenames inside are mangled), so we need
  * to delay restorecon of those until vold explicitly requests it. */
 // NOTE: these paths need to be kept in sync with vold
-#define DATA_SYSTEM_CE_PREFIX "/data/system_ce"
-#define DATA_VENDOR_CE_PREFIX "/data/vendor_ce"
-#define DATA_MISC_CE_PREFIX "/data/misc_ce"
+#define DATA_SYSTEM_CE_PATH "/data/system_ce"
+#define DATA_VENDOR_CE_PATH "/data/vendor_ce"
+#define DATA_MISC_CE_PATH "/data/misc_ce"
+#define DATA_MISC_DE_PATH "/data/misc_de"
 
 /* The path prefixes of package data directories. */
 #define DATA_DATA_PATH "/data/data"
 #define DATA_USER_PATH "/data/user"
 #define DATA_USER_DE_PATH "/data/user_de"
-#define EXPAND_USER_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?/user"
-#define EXPAND_USER_DE_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?/user_de"
 #define USER_PROFILE_PATH "/data/misc/profiles/cur/*"
+#define SDK_SANDBOX_DATA_CE_PATH "/data/misc_ce/*/sdksandbox"
+#define SDK_SANDBOX_DATA_DE_PATH "/data/misc_de/*/sdksandbox"
+
+#define EXPAND_MNT_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?"
+#define EXPAND_USER_PATH EXPAND_MNT_PATH "/user"
+#define EXPAND_USER_DE_PATH EXPAND_MNT_PATH "/user_de"
+#define EXPAND_SDK_CE_PATH EXPAND_MNT_PATH "/misc_ce/*/sdksandbox"
+#define EXPAND_SDK_DE_PATH EXPAND_MNT_PATH "/misc_de/*/sdksandbox"
+
 #define DATA_DATA_PREFIX DATA_DATA_PATH "/"
 #define DATA_USER_PREFIX DATA_USER_PATH "/"
 #define DATA_USER_DE_PREFIX DATA_USER_DE_PATH "/"
+#define DATA_MISC_CE_PREFIX DATA_MISC_CE_PATH "/"
+#define DATA_MISC_DE_PREFIX DATA_MISC_DE_PATH "/"
+#define EXPAND_MNT_PATH_PREFIX EXPAND_MNT_PATH "/"
+
+/*
+ * This method helps in identifying paths that refer to users' app data. Labeling for app data is
+ * based on seapp_contexts and seinfo assignments rather than file_contexts and is managed by
+ * installd rather than by init.
+ */
+static bool is_app_data_path(const char *pathname) {
+    int flags = FNM_LEADING_DIR|FNM_PATHNAME;
+    return (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
+        !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
+        !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
+        !fnmatch(EXPAND_USER_PATH, pathname, flags) ||
+        !fnmatch(EXPAND_USER_DE_PATH, pathname, flags) ||
+        !fnmatch(SDK_SANDBOX_DATA_CE_PATH, pathname, flags) ||
+        !fnmatch(SDK_SANDBOX_DATA_DE_PATH, pathname, flags) ||
+        !fnmatch(EXPAND_SDK_CE_PATH, pathname, flags) ||
+        !fnmatch(EXPAND_SDK_DE_PATH, pathname, flags));
+}
 
 static int pkgdir_selabel_lookup(const char *pathname,
                                  const char *seinfo,
@@ -1180,6 +1212,40 @@
             pathname++;
         else
             return 0;
+    } else if (!strncmp(pathname, DATA_MISC_CE_PREFIX, sizeof(DATA_MISC_CE_PREFIX)-1)) {
+        pathname += sizeof(DATA_MISC_CE_PREFIX) - 1;
+        while (isdigit(*pathname))
+            pathname++;
+        if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) {
+            pathname += sizeof("/sdksandbox/") - 1;
+        } else
+            return 0;
+    } else if (!strncmp(pathname, DATA_MISC_DE_PREFIX, sizeof(DATA_MISC_DE_PREFIX)-1)) {
+        pathname += sizeof(DATA_MISC_DE_PREFIX) - 1;
+        while (isdigit(*pathname))
+            pathname++;
+        if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) {
+            pathname += sizeof("/sdksandbox/") - 1;
+        } else
+            return 0;
+    } else if (!fnmatch(EXPAND_SDK_CE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+        pathname += sizeof(EXPAND_MNT_PATH_PREFIX) - 1;
+        pathname += sizeof("misc_ce/") - 1;
+        while (isdigit(*pathname))
+            pathname++;
+        if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) {
+            pathname += sizeof("/sdksandbox/") - 1;
+        } else
+            return 0;
+    } else if (!fnmatch(EXPAND_SDK_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+        pathname += sizeof(EXPAND_MNT_PATH_PREFIX) - 1;
+        pathname += sizeof("misc_de/") - 1;
+        while (isdigit(*pathname))
+            pathname++;
+        if (!strncmp(pathname, "/sdksandbox/", sizeof("/sdksandbox/")-1)) {
+            pathname += sizeof("/sdksandbox/") - 1;
+        } else
+            return 0;
     } else
         return 0;
 
@@ -1268,11 +1334,7 @@
      * have different labeling rules, based off of /seapp_contexts, and
      * installd is responsible for managing these labels instead of init.
      */
-    if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
-        !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
-        !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
-        !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) ||
-        !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+    if (is_app_data_path(pathname)) {
         if (pkgdir_selabel_lookup(pathname, seinfo, uid, &secontext) < 0)
             goto err;
     }
@@ -1435,11 +1497,7 @@
      * assignments rather than file_contexts and is managed by
      * installd rather than init.
      */
-    if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
-        !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
-        !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
-        !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) ||
-        !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME))
+    if (is_app_data_path(pathname))
         setrestoreconlast = false;
 
     /* Also ignore on /sys since it is regenerated on each boot regardless. */
@@ -1516,20 +1574,15 @@
             }
 
             if (skipce &&
-                (!strncmp(ftsent->fts_path, DATA_SYSTEM_CE_PREFIX, sizeof(DATA_SYSTEM_CE_PREFIX)-1) ||
-                 !strncmp(ftsent->fts_path, DATA_MISC_CE_PREFIX, sizeof(DATA_MISC_CE_PREFIX)-1) ||
-                 !strncmp(ftsent->fts_path, DATA_VENDOR_CE_PREFIX, sizeof(DATA_VENDOR_CE_PREFIX)-1))) {
+                (!strncmp(ftsent->fts_path, DATA_SYSTEM_CE_PATH, sizeof(DATA_SYSTEM_CE_PATH)-1) ||
+                 !strncmp(ftsent->fts_path, DATA_MISC_CE_PATH, sizeof(DATA_MISC_CE_PATH)-1) ||
+                 !strncmp(ftsent->fts_path, DATA_VENDOR_CE_PATH, sizeof(DATA_VENDOR_CE_PATH)-1))) {
                 // Don't label anything below this directory.
                 fts_set(fts, ftsent, FTS_SKIP);
                 // but fall through and make sure we label the directory itself
             }
 
-            if (!datadata &&
-                (!strcmp(ftsent->fts_path, DATA_DATA_PATH) ||
-                 !strncmp(ftsent->fts_path, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
-                 !strncmp(ftsent->fts_path, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
-                 !fnmatch(EXPAND_USER_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME) ||
-                 !fnmatch(EXPAND_USER_DE_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME))) {
+            if (!datadata && is_app_data_path(ftsent->fts_path)) {
                 // Don't label anything below this directory.
                 fts_set(fts, ftsent, FTS_SKIP);
                 // but fall through and make sure we label the directory itself
diff --git a/libselinux/src/callbacks.c b/libselinux/src/callbacks.c
index c18ccc5..469c405 100644
--- a/libselinux/src/callbacks.c
+++ b/libselinux/src/callbacks.c
@@ -10,6 +10,8 @@
 #include <selinux/selinux.h>
 #include "callbacks.h"
 
+pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
+
 /* default implementations */
 static int __attribute__ ((format(printf, 2, 3)))
 default_selinux_log(int type __attribute__((unused)), const char *fmt, ...)
@@ -56,7 +58,7 @@
 
 /* callback pointers */
 int __attribute__ ((format(printf, 2, 3)))
-(*selinux_log)(int, const char *, ...) =
+(*selinux_log_direct)(int, const char *, ...) =
 	default_selinux_log;
 
 int
@@ -81,7 +83,7 @@
 {
 	switch (type) {
 	case SELINUX_CB_LOG:
-		selinux_log = cb.func_log;
+		selinux_log_direct = cb.func_log;
 		break;
 	case SELINUX_CB_AUDIT:
 		selinux_audit = cb.func_audit;
@@ -106,7 +108,7 @@
 
 	switch (type) {
 	case SELINUX_CB_LOG:
-		cb.func_log = selinux_log;
+		cb.func_log = selinux_log_direct;
 		break;
 	case SELINUX_CB_AUDIT:
 		cb.func_audit = selinux_audit;
diff --git a/libselinux/src/callbacks.h b/libselinux/src/callbacks.h
index 03d87f0..f4dab15 100644
--- a/libselinux/src/callbacks.h
+++ b/libselinux/src/callbacks.h
@@ -10,9 +10,11 @@
 #include <string.h>
 #include <selinux/selinux.h>
 
+#include "selinux_internal.h"
+
 /* callback pointers */
 extern int __attribute__ ((format(printf, 2, 3)))
-(*selinux_log) (int type, const char *, ...) ;
+(*selinux_log_direct) (int type, const char *, ...) ;
 
 extern int
 (*selinux_audit) (void *, security_class_t, char *, size_t) ;
@@ -26,4 +28,13 @@
 extern int
 (*selinux_netlink_policyload) (int seqno) ;
 
+/* Thread-safe selinux_log() function */
+extern pthread_mutex_t log_mutex;
+
+#define selinux_log(type, ...) do { \
+	__pthread_mutex_lock(&log_mutex); \
+	selinux_log_direct(type, __VA_ARGS__); \
+	__pthread_mutex_unlock(&log_mutex); \
+} while(0)
+
 #endif				/* _SELINUX_CALLBACKS_H_ */
diff --git a/libselinux/src/is_customizable_type.c b/libselinux/src/is_customizable_type.c
index 1b17860..f83e1e8 100644
--- a/libselinux/src/is_customizable_type.c
+++ b/libselinux/src/is_customizable_type.c
@@ -9,7 +9,10 @@
 #include "selinux_internal.h"
 #include "context_internal.h"
 
-static int get_customizable_type_list(char *** retlist)
+static char **customizable_list = NULL;
+static pthread_once_t customizable_once = PTHREAD_ONCE_INIT;
+
+static void customizable_init(void)
 {
 	FILE *fp;
 	char *buf;
@@ -18,12 +21,12 @@
 
 	fp = fopen(selinux_customizable_types_path(), "re");
 	if (!fp)
-		return -1;
+		return;
 
 	buf = malloc(selinux_page_size);
 	if (!buf) {
 		fclose(fp);
-		return -1;
+		return;
 	}
 	while (fgets_unlocked(buf, selinux_page_size, fp) && ctr < UINT_MAX) {
 		ctr++;
@@ -54,23 +57,19 @@
 	fclose(fp);
 	free(buf);
 	if (!list)
-		return -1;
-	*retlist = list;
-	return 0;
+		return;
+	customizable_list = list;
 }
 
-static char **customizable_list = NULL;
-
 int is_context_customizable(const char * scontext)
 {
 	int i;
 	const char *type;
 	context_t c;
 
-	if (!customizable_list) {
-		if (get_customizable_type_list(&customizable_list) != 0)
-			return -1;
-	}
+	__selinux_once(customizable_once, customizable_init);
+	if (!customizable_list)
+		return -1;
 
 	c = context_new(scontext);
 	if (!c)
diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
index 70ea86f..695d6a9 100644
--- a/libselinux/src/label_file.c
+++ b/libselinux/src/label_file.c
@@ -997,7 +997,12 @@
 			rc = regex_match(spec->regex, key, partial);
 			if (rc == REGEX_MATCH || (partial && rc == REGEX_MATCH_PARTIAL)) {
 				if (rc == REGEX_MATCH) {
-					spec->matches++;
+#ifdef __ATOMIC_RELAXED
+					__atomic_store_n(&spec->any_matches,
+							 true, __ATOMIC_RELAXED);
+#else
+#error "Please use a compiler that supports __atomic builtins"
+#endif
 				}
 
 				if (strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
@@ -1295,9 +1300,15 @@
 	struct saved_data *data = (struct saved_data *)rec->data;
 	unsigned int i, nspec = data->nspec;
 	struct spec *spec_arr = data->spec_arr;
+	bool any_matches;
 
 	for (i = 0; i < nspec; i++) {
-		if (spec_arr[i].matches == 0) {
+#ifdef __ATOMIC_RELAXED
+		any_matches = __atomic_load_n(&spec_arr[i].any_matches, __ATOMIC_RELAXED);
+#else
+#error "Please use a compiler that supports __atomic builtins"
+#endif
+		if (!any_matches) {
 			if (spec_arr[i].type_str) {
 				COMPAT_LOG(SELINUX_WARNING,
 				    "Warning!  No matches for (%s, %s, %s)\n",
diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h
index 343ffc7..b453e13 100644
--- a/libselinux/src/label_file.h
+++ b/libselinux/src/label_file.h
@@ -51,7 +51,7 @@
 	bool regex_compiled; /* bool to indicate if the regex is compiled */
 	pthread_mutex_t regex_lock; /* lock for lazy compilation of regex */
 	mode_t mode;		/* mode format value */
-	int matches;		/* number of matching pathnames */
+	bool any_matches;	/* did any pathname match? */
 	int stem_id;		/* indicates which stem-compression item */
 	char hasMetaChars;	/* regular expression has meta-chars */
 	char from_mmap;		/* this spec is from an mmap of the data */
diff --git a/libselinux/src/libselinux.map b/libselinux/src/libselinux.map
index 2a368e9..4acf1ca 100644
--- a/libselinux/src/libselinux.map
+++ b/libselinux/src/libselinux.map
@@ -240,3 +240,8 @@
   local:
     *;
 };
+
+LIBSELINUX_3.4 {
+  global:
+    selinux_restorecon_parallel;
+} LIBSELINUX_1.0;
diff --git a/libselinux/src/matchpathcon.c b/libselinux/src/matchpathcon.c
index 1e7f889..ea78a23 100644
--- a/libselinux/src/matchpathcon.c
+++ b/libselinux/src/matchpathcon.c
@@ -356,7 +356,7 @@
 		mycanoncon = default_canoncon;
 
 	__selinux_once(once, matchpathcon_init_once);
-	__selinux_setspecific(destructor_key, (void *)1);
+	__selinux_setspecific(destructor_key, /* some valid address to please GCC */ &selinux_page_size);
 
 	options[SELABEL_OPT_SUBSET].type = SELABEL_OPT_SUBSET;
 	options[SELABEL_OPT_SUBSET].value = subset;
diff --git a/libselinux/src/procattr.c b/libselinux/src/procattr.c
index 6552ee0..142fbf3 100644
--- a/libselinux/src/procattr.c
+++ b/libselinux/src/procattr.c
@@ -68,7 +68,7 @@
 static inline void init_thread_destructor(void)
 {
 	if (destructor_initialized == 0) {
-		__selinux_setspecific(destructor_key, (void *)1);
+		__selinux_setspecific(destructor_key, /* some valid address to please GCC */ &selinux_page_size);
 		destructor_initialized = 1;
 	}
 }
diff --git a/libselinux/src/selinux_config.c b/libselinux/src/selinux_config.c
index 97f81a8..d2e49ee 100644
--- a/libselinux/src/selinux_config.c
+++ b/libselinux/src/selinux_config.c
@@ -92,6 +92,7 @@
 	FILE *cfg = fopen(SELINUXCONFIG, "re");
 	if (cfg) {
 		char *buf;
+		char *tag;
 		int len = sizeof(SELINUXTAG) - 1;
 		buf = malloc(selinux_page_size);
 		if (!buf) {
@@ -101,21 +102,24 @@
 		while (fgets_unlocked(buf, selinux_page_size, cfg)) {
 			if (strncmp(buf, SELINUXTAG, len))
 				continue;
+			tag = buf+len;
+			while (isspace(*tag))
+				tag++;
 			if (!strncasecmp
-			    (buf + len, "enforcing", sizeof("enforcing") - 1)) {
+			    (tag, "enforcing", sizeof("enforcing") - 1)) {
 				*enforce = 1;
 				ret = 0;
 				break;
 			} else
 			    if (!strncasecmp
-				(buf + len, "permissive",
+				(tag, "permissive",
 				 sizeof("permissive") - 1)) {
 				*enforce = 0;
 				ret = 0;
 				break;
 			} else
 			    if (!strncasecmp
-				(buf + len, "disabled",
+				(tag, "disabled",
 				 sizeof("disabled") - 1)) {
 				*enforce = -1;
 				ret = 0;
@@ -176,7 +180,10 @@
 
 			if (!strncasecmp(buf_p, SELINUXTYPETAG,
 					 sizeof(SELINUXTYPETAG) - 1)) {
-				type = strdup(buf_p + sizeof(SELINUXTYPETAG) - 1);
+				buf_p += sizeof(SELINUXTYPETAG) - 1;
+				while (isspace(*buf_p))
+					buf_p++;
+				type = strdup(buf_p);
 				if (!type) {
 					free(line_buf);
 					fclose(fp);
@@ -199,6 +206,8 @@
 			} else if (!strncmp(buf_p, REQUIRESEUSERS,
 					    sizeof(REQUIRESEUSERS) - 1)) {
 				value = buf_p + sizeof(REQUIRESEUSERS) - 1;
+				while (isspace(*value))
+					value++;
 				intptr = &require_seusers;
 			} else {
 				continue;
diff --git a/libselinux/src/selinux_internal.h b/libselinux/src/selinux_internal.h
index 27e9ac5..297dcf2 100644
--- a/libselinux/src/selinux_internal.h
+++ b/libselinux/src/selinux_internal.h
@@ -69,6 +69,22 @@
 			pthread_mutex_unlock(LOCK);		\
 	} while (0)
 
+#pragma weak pthread_create
+#pragma weak pthread_join
+#pragma weak pthread_cond_init
+#pragma weak pthread_cond_signal
+#pragma weak pthread_cond_destroy
+#pragma weak pthread_cond_wait
+
+/* check if all functions needed to do parallel operations are available */
+#define __pthread_supported (					\
+	pthread_create &&					\
+	pthread_join &&						\
+	pthread_cond_init &&					\
+	pthread_cond_destroy &&					\
+	pthread_cond_signal &&					\
+	pthread_cond_wait					\
+)
 
 #define SELINUXDIR "/etc/selinux/"
 #define SELINUXCONFIG SELINUXDIR "config"
diff --git a/libselinux/src/selinux_restorecon.c b/libselinux/src/selinux_restorecon.c
index 04d9565..72f4fb4 100644
--- a/libselinux/src/selinux_restorecon.c
+++ b/libselinux/src/selinux_restorecon.c
@@ -60,6 +60,7 @@
 static struct edir *exclude_lst = NULL;
 static uint64_t fc_count = 0;	/* Number of files processed so far */
 static uint64_t efile_count;	/* Estimated total number of files */
+static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 /* Store information on directories with xattr's. */
 static struct dir_xattr *dir_xattr_list;
@@ -411,6 +412,7 @@
 } file_spec_t;
 
 static file_spec_t *fl_head;
+static pthread_mutex_t fl_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 /*
  * Try to add an association between an inode and a context. If there is a
@@ -424,11 +426,12 @@
 	int h, ret;
 	struct stat64 sb;
 
+	__pthread_mutex_lock(&fl_mutex);
+
 	if (!fl_head) {
-		fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS);
+		fl_head = calloc(HASH_BUCKETS, sizeof(file_spec_t));
 		if (!fl_head)
 			goto oom;
-		memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS);
 	}
 
 	h = (ino + (ino >> HASH_BITS)) & HASH_MASK;
@@ -445,11 +448,11 @@
 				fl->con = strdup(con);
 				if (!fl->con)
 					goto oom;
-				return 1;
+				goto unlock_1;
 			}
 
 			if (strcmp(fl->con, con) == 0)
-				return 1;
+				goto unlock_1;
 
 			selinux_log(SELINUX_ERROR,
 				"conflicting specifications for %s and %s, using %s.\n",
@@ -458,6 +461,9 @@
 			fl->file = strdup(file);
 			if (!fl->file)
 				goto oom;
+
+			__pthread_mutex_unlock(&fl_mutex);
+
 			if (flags->conflicterror) {
 				selinux_log(SELINUX_ERROR,
 				"treating conflicting specifications as an error.\n");
@@ -482,13 +488,19 @@
 		goto oom_freefl;
 	fl->next = prevfl->next;
 	prevfl->next = fl;
+
+	__pthread_mutex_unlock(&fl_mutex);
 	return 0;
 
 oom_freefl:
 	free(fl);
 oom:
+	__pthread_mutex_unlock(&fl_mutex);
 	selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
 	return -1;
+unlock_1:
+	__pthread_mutex_unlock(&fl_mutex);
+	return 1;
 }
 
 /*
@@ -598,7 +610,7 @@
 }
 
 static int restorecon_sb(const char *pathname, const struct stat *sb,
-			    struct rest_flags *flags)
+			    struct rest_flags *flags, bool first)
 {
 	char *newcon = NULL;
 	char *curcon = NULL;
@@ -627,7 +639,7 @@
 						    sb->st_mode);
 
 	if (rc < 0) {
-		if (errno == ENOENT && flags->warnonnomatch)
+		if (errno == ENOENT && flags->warnonnomatch && first)
 			selinux_log(SELINUX_INFO,
 				    "Warning no default label for %s\n",
 				    lookup_path);
@@ -636,6 +648,7 @@
 	}
 
 	if (flags->progress) {
+		__pthread_mutex_lock(&progress_mutex);
 		fc_count++;
 		if (fc_count % STAR_COUNT == 0) {
 			if (flags->mass_relabel && efile_count > 0) {
@@ -647,6 +660,7 @@
 			}
 			fflush(stdout);
 		}
+		__pthread_mutex_unlock(&progress_mutex);
 	}
 
 	if (flags->add_assoc) {
@@ -800,66 +814,215 @@
 	goto free;
 }
 
-
-/*
- * Public API
- */
-
-/* selinux_restorecon(3) - Main function that is responsible for labeling */
-int selinux_restorecon(const char *pathname_orig,
-		       unsigned int restorecon_flags)
-{
+struct rest_state {
 	struct rest_flags flags;
+	dev_t dev_num;
+	struct statfs sfsb;
+	bool ignore_digest;
+	bool setrestorecondigest;
+	bool parallel;
 
-	flags.nochange = (restorecon_flags &
+	FTS *fts;
+	FTSENT *ftsent_first;
+	struct dir_hash_node *head, *current;
+	bool abort;
+	int error;
+	int saved_errno;
+	pthread_mutex_t mutex;
+};
+
+static void *selinux_restorecon_thread(void *arg)
+{
+	struct rest_state *state = arg;
+	FTS *fts = state->fts;
+	FTSENT *ftsent;
+	int error;
+	char ent_path[PATH_MAX];
+	struct stat ent_st;
+	bool first = false;
+
+	if (state->parallel)
+		pthread_mutex_lock(&state->mutex);
+
+	if (state->ftsent_first) {
+		ftsent = state->ftsent_first;
+		state->ftsent_first = NULL;
+		first = true;
+		goto loop_body;
+	}
+
+	while (((void)(errno = 0), ftsent = fts_read(fts)) != NULL) {
+loop_body:
+		/* If the FTS_XDEV flag is set and the device is different */
+		if (state->flags.set_xdev &&
+		    ftsent->fts_statp->st_dev != state->dev_num)
+			continue;
+
+		switch (ftsent->fts_info) {
+		case FTS_DC:
+			selinux_log(SELINUX_ERROR,
+				    "Directory cycle on %s.\n",
+				    ftsent->fts_path);
+			errno = ELOOP;
+			state->error = -1;
+			state->abort = true;
+			goto finish;
+		case FTS_DP:
+			continue;
+		case FTS_DNR:
+			error = errno;
+			errno = ftsent->fts_errno;
+			selinux_log(SELINUX_ERROR,
+				    "Could not read %s: %m.\n",
+				    ftsent->fts_path);
+			errno = error;
+			fts_set(fts, ftsent, FTS_SKIP);
+			continue;
+		case FTS_NS:
+			error = errno;
+			errno = ftsent->fts_errno;
+			selinux_log(SELINUX_ERROR,
+				    "Could not stat %s: %m.\n",
+				    ftsent->fts_path);
+			errno = error;
+			fts_set(fts, ftsent, FTS_SKIP);
+			continue;
+		case FTS_ERR:
+			error = errno;
+			errno = ftsent->fts_errno;
+			selinux_log(SELINUX_ERROR,
+				    "Error on %s: %m.\n",
+				    ftsent->fts_path);
+			errno = error;
+			fts_set(fts, ftsent, FTS_SKIP);
+			continue;
+		case FTS_D:
+			if (state->sfsb.f_type == SYSFS_MAGIC &&
+			    !selabel_partial_match(fc_sehandle,
+			    ftsent->fts_path)) {
+				fts_set(fts, ftsent, FTS_SKIP);
+				continue;
+			}
+
+			if (check_excluded(ftsent->fts_path)) {
+				fts_set(fts, ftsent, FTS_SKIP);
+				continue;
+			}
+
+			if (state->setrestorecondigest) {
+				struct dir_hash_node *new_node = NULL;
+
+				if (check_context_match_for_dir(ftsent->fts_path,
+								&new_node,
+								state->error) &&
+								!state->ignore_digest) {
+					selinux_log(SELINUX_INFO,
+						"Skipping restorecon on directory(%s)\n",
+						    ftsent->fts_path);
+					fts_set(fts, ftsent, FTS_SKIP);
+					continue;
+				}
+
+				if (new_node && !state->error) {
+					if (!state->current) {
+						state->current = new_node;
+						state->head = state->current;
+					} else {
+						state->current->next = new_node;
+						state->current = new_node;
+					}
+				}
+			}
+			/* fall through */
+		default:
+			strcpy(ent_path, ftsent->fts_path);
+			ent_st = *ftsent->fts_statp;
+			if (state->parallel)
+				pthread_mutex_unlock(&state->mutex);
+
+			error = restorecon_sb(ent_path, &ent_st, &state->flags,
+					      first);
+
+			if (state->parallel) {
+				pthread_mutex_lock(&state->mutex);
+				if (state->abort)
+					goto unlock;
+			}
+
+			state->error |= error;
+			first = false;
+			if (error && state->flags.abort_on_error) {
+				state->abort = true;
+				goto finish;
+			}
+			break;
+		}
+	}
+
+finish:
+	if (!state->saved_errno)
+		state->saved_errno = errno;
+unlock:
+	if (state->parallel)
+		pthread_mutex_unlock(&state->mutex);
+	return NULL;
+}
+
+static int selinux_restorecon_common(const char *pathname_orig,
+				     unsigned int restorecon_flags,
+				     size_t nthreads)
+{
+	struct rest_state state;
+
+	state.flags.nochange = (restorecon_flags &
 		    SELINUX_RESTORECON_NOCHANGE) ? true : false;
-	flags.verbose = (restorecon_flags &
+	state.flags.verbose = (restorecon_flags &
 		    SELINUX_RESTORECON_VERBOSE) ? true : false;
-	flags.progress = (restorecon_flags &
+	state.flags.progress = (restorecon_flags &
 		    SELINUX_RESTORECON_PROGRESS) ? true : false;
-	flags.mass_relabel = (restorecon_flags &
+	state.flags.mass_relabel = (restorecon_flags &
 		    SELINUX_RESTORECON_MASS_RELABEL) ? true : false;
-	flags.recurse = (restorecon_flags &
+	state.flags.recurse = (restorecon_flags &
 		    SELINUX_RESTORECON_RECURSE) ? true : false;
-	flags.set_specctx = (restorecon_flags &
+	state.flags.set_specctx = (restorecon_flags &
 		    SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false;
-	flags.userealpath = (restorecon_flags &
+	state.flags.userealpath = (restorecon_flags &
 		   SELINUX_RESTORECON_REALPATH) ? true : false;
-	flags.set_xdev = (restorecon_flags &
+	state.flags.set_xdev = (restorecon_flags &
 		   SELINUX_RESTORECON_XDEV) ? true : false;
-	flags.add_assoc = (restorecon_flags &
+	state.flags.add_assoc = (restorecon_flags &
 		   SELINUX_RESTORECON_ADD_ASSOC) ? true : false;
-	flags.abort_on_error = (restorecon_flags &
+	state.flags.abort_on_error = (restorecon_flags &
 		   SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false;
-	flags.syslog_changes = (restorecon_flags &
+	state.flags.syslog_changes = (restorecon_flags &
 		   SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false;
-	flags.log_matches = (restorecon_flags &
+	state.flags.log_matches = (restorecon_flags &
 		   SELINUX_RESTORECON_LOG_MATCHES) ? true : false;
-	flags.ignore_noent = (restorecon_flags &
+	state.flags.ignore_noent = (restorecon_flags &
 		   SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false;
-	flags.warnonnomatch = true;
-	flags.conflicterror = (restorecon_flags &
+	state.flags.warnonnomatch = true;
+	state.flags.conflicterror = (restorecon_flags &
 		   SELINUX_RESTORECON_CONFLICT_ERROR) ? true : false;
 	ignore_mounts = (restorecon_flags &
 		   SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false;
-	bool ignore_digest = (restorecon_flags &
+	state.ignore_digest = (restorecon_flags &
 		    SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
-	bool setrestorecondigest = true;
+	state.setrestorecondigest = true;
+
+	state.head = NULL;
+	state.current = NULL;
+	state.abort = false;
+	state.error = 0;
+	state.saved_errno = 0;
 
 	struct stat sb;
-	struct statfs sfsb;
-	FTS *fts;
-	FTSENT *ftsent;
 	char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
 	char *paths[2] = { NULL, NULL };
 	int fts_flags, error, sverrno;
-	dev_t dev_num = 0;
 	struct dir_hash_node *current = NULL;
-	struct dir_hash_node *head = NULL;
-	int errno_tmp;
 
-	if (flags.verbose && flags.progress)
-		flags.verbose = false;
+	if (state.flags.verbose && state.flags.progress)
+		state.flags.verbose = false;
 
 	__selinux_once(fc_once, restorecon_init);
 
@@ -872,13 +1035,31 @@
 	 */
 	if (selabel_no_digest ||
 	    (restorecon_flags & SELINUX_RESTORECON_SKIP_DIGEST))
-		setrestorecondigest = false;
+		state.setrestorecondigest = false;
+
+	if (!__pthread_supported) {
+		if (nthreads != 1) {
+			nthreads = 1;
+			selinux_log(SELINUX_WARNING,
+				"Threading functionality not available, falling back to 1 thread.");
+		}
+	} else if (nthreads == 0) {
+		long nproc = sysconf(_SC_NPROCESSORS_ONLN);
+
+		if (nproc > 0) {
+			nthreads = nproc;
+		} else {
+			nthreads = 1;
+			selinux_log(SELINUX_WARNING,
+				"Unable to detect CPU count, falling back to 1 thread.");
+		}
+	}
 
 	/*
 	 * Convert passed-in pathname to canonical pathname by resolving
 	 * realpath of containing dir, then appending last component name.
 	 */
-	if (flags.userealpath) {
+	if (state.flags.userealpath) {
 		char *basename_cpy = strdup(pathname_orig);
 		if (!basename_cpy)
 			goto realpatherr;
@@ -923,7 +1104,7 @@
 	paths[0] = pathname;
 
 	if (lstat(pathname, &sb) < 0) {
-		if (flags.ignore_noent && errno == ENOENT) {
+		if (state.flags.ignore_noent && errno == ENOENT) {
 			free(pathdnamer);
 			free(pathname);
 			return 0;
@@ -938,21 +1119,21 @@
 
 	/* Skip digest if not a directory */
 	if (!S_ISDIR(sb.st_mode))
-		setrestorecondigest = false;
+		state.setrestorecondigest = false;
 
-	if (!flags.recurse) {
+	if (!state.flags.recurse) {
 		if (check_excluded(pathname)) {
 			error = 0;
 			goto cleanup;
 		}
 
-		error = restorecon_sb(pathname, &sb, &flags);
+		error = restorecon_sb(pathname, &sb, &state.flags, true);
 		goto cleanup;
 	}
 
 	/* Obtain fs type */
-	memset(&sfsb, 0, sizeof sfsb);
-	if (!S_ISLNK(sb.st_mode) && statfs(pathname, &sfsb) < 0) {
+	memset(&state.sfsb, 0, sizeof(state.sfsb));
+	if (!S_ISLNK(sb.st_mode) && statfs(pathname, &state.sfsb) < 0) {
 		selinux_log(SELINUX_ERROR,
 			    "statfs(%s) failed: %m\n",
 			    pathname);
@@ -961,21 +1142,21 @@
 	}
 
 	/* Skip digest on in-memory filesystems and /sys */
-	if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC ||
-	    sfsb.f_type == SYSFS_MAGIC)
-		setrestorecondigest = false;
+	if (state.sfsb.f_type == RAMFS_MAGIC || state.sfsb.f_type == TMPFS_MAGIC ||
+	    state.sfsb.f_type == SYSFS_MAGIC)
+		state.setrestorecondigest = false;
 
-	if (flags.set_xdev)
+	if (state.flags.set_xdev)
 		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV;
 	else
 		fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
 
-	fts = fts_open(paths, fts_flags, NULL);
-	if (!fts)
+	state.fts = fts_open(paths, fts_flags, NULL);
+	if (!state.fts)
 		goto fts_err;
 
-	ftsent = fts_read(fts);
-	if (!ftsent)
+	state.ftsent_first = fts_read(state.fts);
+	if (!state.ftsent_first)
 		goto fts_err;
 
 	/*
@@ -987,106 +1168,66 @@
 	 * directories with a different device number when the FTS_XDEV flag
 	 * is set (from http://marc.info/?l=selinux&m=124688830500777&w=2).
 	 */
-	dev_num = ftsent->fts_statp->st_dev;
+	state.dev_num = state.ftsent_first->fts_statp->st_dev;
 
-	error = 0;
-	do {
-		/* If the FTS_XDEV flag is set and the device is different */
-		if (flags.set_xdev && ftsent->fts_statp->st_dev != dev_num)
-			continue;
+	if (nthreads == 1) {
+		state.parallel = false;
+		selinux_restorecon_thread(&state);
+	} else {
+		size_t i;
+		pthread_t self = pthread_self();
+		pthread_t *threads = NULL;
 
-		switch (ftsent->fts_info) {
-		case FTS_DC:
-			selinux_log(SELINUX_ERROR,
-				    "Directory cycle on %s.\n",
-				    ftsent->fts_path);
-			errno = ELOOP;
-			error = -1;
-			goto out;
-		case FTS_DP:
-			continue;
-		case FTS_DNR:
-			errno_tmp = errno;
-			errno = ftsent->fts_errno;
-			selinux_log(SELINUX_ERROR,
-				    "Could not read %s: %m.\n",
-				    ftsent->fts_path);
-			errno = errno_tmp;
-			fts_set(fts, ftsent, FTS_SKIP);
-			continue;
-		case FTS_NS:
-			errno_tmp = errno;
-			errno = ftsent->fts_errno;
-			selinux_log(SELINUX_ERROR,
-				    "Could not stat %s: %m.\n",
-				    ftsent->fts_path);
-			errno = errno_tmp;
-			fts_set(fts, ftsent, FTS_SKIP);
-			continue;
-		case FTS_ERR:
-			errno_tmp = errno;
-			errno = ftsent->fts_errno;
-			selinux_log(SELINUX_ERROR,
-				    "Error on %s: %m.\n",
-				    ftsent->fts_path);
-			errno = errno_tmp;
-			fts_set(fts, ftsent, FTS_SKIP);
-			continue;
-		case FTS_D:
-			if (sfsb.f_type == SYSFS_MAGIC &&
-			    !selabel_partial_match(fc_sehandle,
-			    ftsent->fts_path)) {
-				fts_set(fts, ftsent, FTS_SKIP);
-				continue;
+		pthread_mutex_init(&state.mutex, NULL);
+
+		threads = calloc(nthreads - 1, sizeof(*threads));
+		if (!threads)
+			goto oom;
+
+		state.parallel = true;
+		/*
+		 * Start (nthreads - 1) threads - the main thread is going to
+		 * take part, too.
+		 */
+		for (i = 0; i < nthreads - 1; i++) {
+			if (pthread_create(&threads[i], NULL,
+					   selinux_restorecon_thread, &state)) {
+				/*
+				 * If any thread fails to be created, just mark
+				 * it as such and let the successfully created
+				 * threads do the job. In the worst case the
+				 * main thread will do everything, but that's
+				 * still better than to give up.
+				 */
+				threads[i] = self;
 			}
-
-			if (check_excluded(ftsent->fts_path)) {
-				fts_set(fts, ftsent, FTS_SKIP);
-				continue;
-			}
-
-			if (setrestorecondigest) {
-				struct dir_hash_node *new_node = NULL;
-
-				if (check_context_match_for_dir(ftsent->fts_path,
-								&new_node,
-								error) &&
-								!ignore_digest) {
-					selinux_log(SELINUX_INFO,
-						    "Skipping restorecon on directory(%s)\n",
-						    ftsent->fts_path);
-					fts_set(fts, ftsent, FTS_SKIP);
-					continue;
-				}
-
-				if (new_node && !error) {
-					if (!current) {
-						current = new_node;
-						head = current;
-					} else {
-						current->next = new_node;
-						current = current->next;
-					}
-				}
-			}
-			/* fall through */
-		default:
-			error |= restorecon_sb(ftsent->fts_path,
-					       ftsent->fts_statp, &flags);
-			if (flags.warnonnomatch)
-				flags.warnonnomatch = false;
-			if (error && flags.abort_on_error)
-				goto out;
-			break;
 		}
-	} while ((ftsent = fts_read(fts)) != NULL);
+
+		/* Let's join in on the fun! */
+		selinux_restorecon_thread(&state);
+
+		/* Now wait for all threads to finish. */
+		for (i = 0; i < nthreads - 1; i++) {
+			/* Skip threads that failed to be created. */
+			if (pthread_equal(threads[i], self))
+				continue;
+			pthread_join(threads[i], NULL);
+		}
+		free(threads);
+
+		pthread_mutex_destroy(&state.mutex);
+	}
+
+	error = state.error;
+	if (state.saved_errno)
+		goto out;
 
 	/*
 	 * Labeling successful. Write partial match digests for subdirectories.
 	 * TODO: Write digest upon FTS_DP if no error occurs in its descents.
 	 */
-	if (setrestorecondigest && !flags.nochange && !error) {
-		current = head;
+	if (state.setrestorecondigest && !state.flags.nochange && !error) {
+		current = state.head;
 		while (current != NULL) {
 			if (setxattr(current->path,
 			    RESTORECON_PARTIAL_MATCH_DIGEST,
@@ -1101,22 +1242,21 @@
 	}
 
 out:
-	if (flags.progress && flags.mass_relabel)
+	if (state.flags.progress && state.flags.mass_relabel)
 		fprintf(stdout, "\r%s 100.0%%\n", pathname);
 
-	sverrno = errno;
-	(void) fts_close(fts);
-	errno = sverrno;
+	(void) fts_close(state.fts);
+	errno = state.saved_errno;
 cleanup:
-	if (flags.add_assoc) {
-		if (flags.verbose)
+	if (state.flags.add_assoc) {
+		if (state.flags.verbose)
 			filespec_eval();
 		filespec_destroy();
 	}
 	free(pathdnamer);
 	free(pathname);
 
-	current = head;
+	current = state.head;
 	while (current != NULL) {
 		struct dir_hash_node *next = current->next;
 
@@ -1150,6 +1290,26 @@
 	goto cleanup;
 }
 
+
+/*
+ * Public API
+ */
+
+/* selinux_restorecon(3) - Main function that is responsible for labeling */
+int selinux_restorecon(const char *pathname_orig,
+		       unsigned int restorecon_flags)
+{
+	return selinux_restorecon_common(pathname_orig, restorecon_flags, 1);
+}
+
+/* selinux_restorecon_parallel(3) - Parallel version of selinux_restorecon(3) */
+int selinux_restorecon_parallel(const char *pathname_orig,
+				unsigned int restorecon_flags,
+				size_t nthreads)
+{
+	return selinux_restorecon_common(pathname_orig, restorecon_flags, nthreads);
+}
+
 /* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */
 void selinux_restorecon_set_sehandle(struct selabel_handle *hndl)
 {
diff --git a/libselinux/src/selinuxswig_python.i b/libselinux/src/selinuxswig_python.i
index 4c73bf9..17e03b9 100644
--- a/libselinux/src/selinuxswig_python.i
+++ b/libselinux/src/selinuxswig_python.i
@@ -20,7 +20,7 @@
 PERMISSIVE = 0
 ENFORCING = 1
 
-def restorecon(path, recursive=False, verbose=False, force=False):
+def restorecon(path, recursive=False, verbose=False, force=False, nthreads=1):
     """ Restore SELinux context on a given path
 
     Arguments:
@@ -32,6 +32,8 @@
     force -- Force reset of context to match file_context for customizable files,
     and the default file context, changing the user, role, range portion  as well
     as the type (default False)
+    nthreads -- The number of threads to use during relabeling, or 0 to use as many
+    threads as there are online CPU cores (default 1)
     """
 
     restorecon_flags = SELINUX_RESTORECON_IGNORE_DIGEST | SELINUX_RESTORECON_REALPATH
@@ -41,7 +43,7 @@
         restorecon_flags |= SELINUX_RESTORECON_VERBOSE
     if force:
         restorecon_flags |= SELINUX_RESTORECON_SET_SPECFILE_CTX
-    selinux_restorecon(os.path.expanduser(path), restorecon_flags)
+    selinux_restorecon_parallel(os.path.expanduser(path), restorecon_flags, nthreads)
 
 def chcon(path, context, recursive=False):
     """ Set the SELinux context on a given path """
diff --git a/libselinux/src/selinuxswig_python_exception.i b/libselinux/src/selinuxswig_python_exception.i
index 237ea69..a02f492 100644
--- a/libselinux/src/selinuxswig_python_exception.i
+++ b/libselinux/src/selinuxswig_python_exception.i
@@ -1183,6 +1183,14 @@
   }
 }
 
+%exception selinux_restorecon_parallel {
+  $action
+  if (result < 0) {
+     PyErr_SetFromErrno(PyExc_OSError);
+     SWIG_fail;
+  }
+}
+
 %exception selinux_restorecon_set_alt_rootpath {
   $action
   if (result < 0) {
diff --git a/libselinux/src/setrans_client.c b/libselinux/src/setrans_client.c
index 52a8ba7..faa1268 100644
--- a/libselinux/src/setrans_client.c
+++ b/libselinux/src/setrans_client.c
@@ -272,7 +272,7 @@
 	if (!has_setrans)
 		return;
 	if (destructor_initialized == 0) {
-		__selinux_setspecific(destructor_key, (void *)1);
+		__selinux_setspecific(destructor_key, /* some valid address to please GCC */ &selinux_page_size);
 		destructor_initialized = 1;
 	}
 }
diff --git a/libselinux/utils/Makefile b/libselinux/utils/Makefile
index 3681615..801066c 100644
--- a/libselinux/utils/Makefile
+++ b/libselinux/utils/Makefile
@@ -44,7 +44,6 @@
 override CFLAGS += -I../include -D_GNU_SOURCE $(DISABLE_FLAGS) $(PCRE_CFLAGS)
 override LDFLAGS += -L../src
 override LDLIBS += -lselinux $(FTS_LDLIBS)
-PCRE_LDLIBS ?= -lpcre
 
 ifeq ($(ANDROID_HOST),y)
 TARGETS=sefcontext_compile
diff --git a/libsemanage/include/semanage/handle.h b/libsemanage/include/semanage/handle.h
index 946d69b..0157be4 100644
--- a/libsemanage/include/semanage/handle.h
+++ b/libsemanage/include/semanage/handle.h
@@ -66,6 +66,11 @@
  * 1 for yes, 0 for no (default) */
 extern void semanage_set_rebuild(semanage_handle_t * handle, int do_rebuild);
 
+/* set whether to rebuild the policy on commit when potential changes
+ * to module files since last rebuild are detected,
+ * 1 for yes (default), 0 for no */
+extern void semanage_set_check_ext_changes(semanage_handle_t * handle, int do_check);
+
 /* Fills *compiler_path with the location of the hll compiler sh->conf->compiler_directory_path
  * corresponding to lang_ext.
  * Upon success returns 0, -1 on error. */
diff --git a/libsemanage/include/semanage/modules.h b/libsemanage/include/semanage/modules.h
index b51f61f..14666f6 100644
--- a/libsemanage/include/semanage/modules.h
+++ b/libsemanage/include/semanage/modules.h
@@ -282,4 +282,30 @@
 				       const semanage_module_key_t *modkey,
 				       int *enabled);
 
+/* Compute checksum for @modkey module contents.
+ *
+ * If @checksum is NULL, the function will just return the length of the
+ * checksum string in @checksum_len (checksum strings are guaranteed to
+ * have a fixed length for a given libsemanage binary). @modkey and @cil
+ * are ignored in this case and should be set to NULL and 0 (respectively).
+ *
+ * If @checksum is non-NULL, on success, @checksum will point to a buffer
+ * containing the checksum string and @checksum_len will point to the
+ * length of the string (without the null terminator). The semantics of
+ * @cil are the same as for @extract_cil in semanage_module_extract().
+ *
+ * The caller is responsible to free the buffer returned in @checksum (using
+ * free(3)).
+ *
+ * Callers may assume that if the checksum strings for two modules match,
+ * the module content is the same (collisions are theoretically possible,
+ * yet extremely unlikely).
+ *
+ * Returns 0 on success and -1 on error.
+ */
+extern int semanage_module_compute_checksum(semanage_handle_t *sh,
+					    semanage_module_key_t *modkey,
+					    int cil, char **checksum,
+					    size_t *checksum_len);
+
 #endif
diff --git a/libsemanage/man/man5/semanage.conf.5 b/libsemanage/man/man5/semanage.conf.5
index 7d6f2fe..380b58b 100644
--- a/libsemanage/man/man5/semanage.conf.5
+++ b/libsemanage/man/man5/semanage.conf.5
@@ -23,7 +23,7 @@
 Otherwise a socket path or a server name can be used for the argument.
 If the argument begins with "/" (as in "/foo/bar"), it represents the path to a named socket that should be used to connect the policy management
 server.
-If the argument does not begin with a "/" (as in "foo.com:4242"), it should be interpreted as the name of a remote policy management server
+If the argument does not begin with a "/" (as in "example.com:4242"), it should be interpreted as the name of a remote policy management server
 to be used through a TCP connection (default port is 4242 unless a different one is specified after the server name using the colon to separate
 the two fields).
 
diff --git a/libsemanage/man/ru/man5/semanage.conf.5 b/libsemanage/man/ru/man5/semanage.conf.5
index cf65b3e..548aa58 100644
--- a/libsemanage/man/ru/man5/semanage.conf.5
+++ b/libsemanage/man/ru/man5/semanage.conf.5
@@ -19,7 +19,7 @@
 Указать, как библиотека управления SELinux должна взаимодействовать с хранилищем политики SELinux. Если установлено "direct", библиотека управления SELinux выполняет запись напрямую в хранилище модулей политики SELinux (это значение по умолчанию).
 В ином случае в качестве аргумента может использоваться путь к сокету или имя сервера.
 Если аргумент начинается с "/" (как в "/foo/bar"), он представляет собой путь к именованному сокету, который следует использовать для подключения сервера управления политикой.
-Если аргумент не начинается с "/" (как в "foo.com:4242"), он должен интерпретироваться как имя удалённого сервера управления политикой, который следует использовать через TCP-подключение (порт по умолчанию 4242, если только после имени сервера через двоеточие, разделяющее два поля, не указан другой порт).
+Если аргумент не начинается с "/" (как в "example.com:4242"), он должен интерпретироваться как имя удалённого сервера управления политикой, который следует использовать через TCP-подключение (порт по умолчанию 4242, если только после имени сервера через двоеточие, разделяющее два поля, не указан другой порт).
 
 .TP
 .B root
diff --git a/libsemanage/src/boolean_record.c b/libsemanage/src/boolean_record.c
index 95f3a86..40dc654 100644
--- a/libsemanage/src/boolean_record.c
+++ b/libsemanage/src/boolean_record.c
@@ -7,6 +7,9 @@
  */
 
 #include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
 #include <sepol/boolean_record.h>
 
 typedef sepol_bool_t semanage_bool_t;
@@ -20,7 +23,6 @@
 #include "boolean_internal.h"
 #include "handle.h"
 #include "database.h"
-#include <stdlib.h>
 #include <selinux/selinux.h>
 
 /* Key */
diff --git a/libsemanage/src/booleans_file.c b/libsemanage/src/booleans_file.c
index f79d0b4..6d600bb 100644
--- a/libsemanage/src/booleans_file.c
+++ b/libsemanage/src/booleans_file.c
@@ -48,7 +48,7 @@
 		goto last;
 
 	/* Extract name */
-	if (parse_fetch_string(handle, info, &str, '=') < 0)
+	if (parse_fetch_string(handle, info, &str, '=', 0) < 0)
 		goto err;
 
 	if (semanage_bool_set_name(handle, boolean, str) < 0)
diff --git a/libsemanage/src/compressed_file.c b/libsemanage/src/compressed_file.c
new file mode 100644
index 0000000..5546b83
--- /dev/null
+++ b/libsemanage/src/compressed_file.c
@@ -0,0 +1,224 @@
+/* Author: Jason Tang	  <jtang@tresys.com>
+ *         Christopher Ashworth <cashworth@tresys.com>
+ *         Ondrej Mosnacek <omosnacek@gmail.com>
+ *
+ * Copyright (C) 2004-2006 Tresys Technology, LLC
+ * Copyright (C) 2005-2021 Red Hat, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <bzlib.h>
+
+#include "compressed_file.h"
+
+#include "debug.h"
+
+#define BZ2_MAGICSTR "BZh"
+#define BZ2_MAGICLEN (sizeof(BZ2_MAGICSTR)-1)
+
+/* bzip() a data to a file, returning the total number of compressed bytes
+ * in the file.  Returns -1 if file could not be compressed. */
+static int bzip(semanage_handle_t *sh, const char *filename, void *data,
+		size_t num_bytes)
+{
+	BZFILE* b;
+	size_t  size = 1<<16;
+	int     bzerror;
+	size_t  total = 0;
+	size_t len = 0;
+	FILE *f;
+
+	if ((f = fopen(filename, "wb")) == NULL) {
+		return -1;
+	}
+
+	if (!sh->conf->bzip_blocksize) {
+		if (fwrite(data, 1, num_bytes, f) < num_bytes) {
+			fclose(f);
+			return -1;
+		}
+		fclose(f);
+		return 0;
+	}
+
+	b = BZ2_bzWriteOpen( &bzerror, f, sh->conf->bzip_blocksize, 0, 0);
+	if (bzerror != BZ_OK) {
+		BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 );
+		fclose(f);
+		return -1;
+	}
+
+	while ( num_bytes > total ) {
+		if (num_bytes - total > size) {
+			len = size;
+		} else {
+			len = num_bytes - total;
+		}
+		BZ2_bzWrite ( &bzerror, b, (uint8_t *)data + total, len );
+		if (bzerror == BZ_IO_ERROR) {
+			BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 );
+			fclose(f);
+			return -1;
+		}
+		total += len;
+	}
+
+	BZ2_bzWriteClose ( &bzerror, b, 0, 0, 0 );
+	fclose(f);
+	if (bzerror == BZ_IO_ERROR) {
+		return -1;
+	}
+	return 0;
+}
+
+/* bunzip() a file to '*data', returning the total number of uncompressed bytes
+ * in the file.  Returns -1 if file could not be decompressed. */
+static ssize_t bunzip(semanage_handle_t *sh, FILE *f, void **data)
+{
+	BZFILE*  b = NULL;
+	size_t   nBuf;
+	uint8_t* buf = NULL;
+	size_t   size = 1<<18;
+	size_t   bufsize = size;
+	int      bzerror;
+	size_t   total = 0;
+	uint8_t* uncompress = NULL;
+	uint8_t* tmpalloc = NULL;
+	int      ret = -1;
+
+	buf = malloc(bufsize);
+	if (buf == NULL) {
+		ERR(sh, "Failure allocating memory.");
+		goto exit;
+	}
+
+	/* Check if the file is bzipped */
+	bzerror = fread(buf, 1, BZ2_MAGICLEN, f);
+	rewind(f);
+	if ((bzerror != BZ2_MAGICLEN) || memcmp(buf, BZ2_MAGICSTR, BZ2_MAGICLEN)) {
+		goto exit;
+	}
+
+	b = BZ2_bzReadOpen ( &bzerror, f, 0, sh->conf->bzip_small, NULL, 0 );
+	if ( bzerror != BZ_OK ) {
+		ERR(sh, "Failure opening bz2 archive.");
+		goto exit;
+	}
+
+	uncompress = malloc(size);
+	if (uncompress == NULL) {
+		ERR(sh, "Failure allocating memory.");
+		goto exit;
+	}
+
+	while ( bzerror == BZ_OK) {
+		nBuf = BZ2_bzRead ( &bzerror, b, buf, bufsize);
+		if (( bzerror == BZ_OK ) || ( bzerror == BZ_STREAM_END )) {
+			if (total + nBuf > size) {
+				size *= 2;
+				tmpalloc = realloc(uncompress, size);
+				if (tmpalloc == NULL) {
+					ERR(sh, "Failure allocating memory.");
+					goto exit;
+				}
+				uncompress = tmpalloc;
+			}
+			memcpy(&uncompress[total], buf, nBuf);
+			total += nBuf;
+		}
+	}
+	if ( bzerror != BZ_STREAM_END ) {
+		ERR(sh, "Failure reading bz2 archive.");
+		goto exit;
+	}
+
+	ret = total;
+	*data = uncompress;
+
+exit:
+	BZ2_bzReadClose ( &bzerror, b );
+	free(buf);
+	if ( ret < 0 ) {
+		free(uncompress);
+	}
+	return ret;
+}
+
+int map_compressed_file(semanage_handle_t *sh, const char *path,
+			struct file_contents *contents)
+{
+	ssize_t size = -1;
+	void *uncompress;
+	int ret = 0, fd = -1;
+	FILE *file = NULL;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1) {
+		ERR(sh, "Unable to open %s\n", path);
+		return -1;
+	}
+
+	file = fdopen(fd, "r");
+	if (file == NULL) {
+		ERR(sh, "Unable to open %s\n", path);
+		close(fd);
+		return -1;
+	}
+
+	if ((size = bunzip(sh, file, &uncompress)) >= 0) {
+		contents->data = uncompress;
+		contents->len = size;
+		contents->compressed = 1;
+	} else {
+		struct stat sb;
+		if (fstat(fd, &sb) == -1 ||
+		    (uncompress = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) ==
+		    MAP_FAILED) {
+			ret = -1;
+		} else {
+			contents->data = uncompress;
+			contents->len = sb.st_size;
+			contents->compressed = 0;
+		}
+	}
+	fclose(file);
+	return ret;
+}
+
+void unmap_compressed_file(struct file_contents *contents)
+{
+	if (!contents->data)
+		return;
+
+	if (contents->compressed) {
+		free(contents->data);
+	} else {
+		munmap(contents->data, contents->len);
+	}
+}
+
+int write_compressed_file(semanage_handle_t *sh, const char *path,
+			  void *data, size_t len)
+{
+	return bzip(sh, path, data, len);
+}
diff --git a/libsemanage/src/compressed_file.h b/libsemanage/src/compressed_file.h
new file mode 100644
index 0000000..96cfb4b
--- /dev/null
+++ b/libsemanage/src/compressed_file.h
@@ -0,0 +1,78 @@
+/* Author: Jason Tang	  <jtang@tresys.com>
+ *         Christopher Ashworth <cashworth@tresys.com>
+ *         Ondrej Mosnacek <omosnacek@gmail.com>
+ *
+ * Copyright (C) 2004-2006 Tresys Technology, LLC
+ * Copyright (C) 2005-2021 Red Hat, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _SEMANAGE_CIL_FILE_H_
+#define _SEMANAGE_CIL_FILE_H_
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include "handle.h"
+
+struct file_contents {
+	void *data; /** file contents (uncompressed) */
+	size_t len; /** length of contents */
+	int compressed; /** whether file was compressed */
+};
+
+/**
+ * Map/read a possibly-compressed file into memory.
+ *
+ * If the file is bzip compressed map_file will uncompress the file into
+ * @p contents. The caller is responsible for calling
+ * @ref unmap_compressed_file on @p contents on success.
+ *
+ * @param sh        semanage handle
+ * @param path      path to the file
+ * @param contents  pointer to struct file_contents, which will be
+ *   populated with data pointer, size, and an indication whether
+ *   the file was compressed or not
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+int map_compressed_file(semanage_handle_t *sh, const char *path,
+			struct file_contents *contents);
+
+/**
+ * Destroy a previously mapped possibly-compressed file.
+ *
+ * If all fields of @p contents are zero/NULL, the function is
+ * guaranteed to do nothing.
+ *
+ * @param contents  pointer to struct file_contents to destroy
+ */
+void unmap_compressed_file(struct file_contents *contents);
+
+/**
+ * Write bytes into a file, using compression if configured.
+ *
+ * @param sh    semanage handle
+ * @param path  path to the file
+ * @param data  pointer to the data
+ * @param len   length of the data
+ *
+ * @return 0 on success, -1 otherwise.
+ */
+int write_compressed_file(semanage_handle_t *sh, const char *path,
+			  void *data, size_t len);
+
+#endif
diff --git a/libsemanage/src/context_record.c b/libsemanage/src/context_record.c
index 16ba518..dafb90f 100644
--- a/libsemanage/src/context_record.c
+++ b/libsemanage/src/context_record.c
@@ -7,6 +7,8 @@
 
 #define _SEMANAGE_CONTEXT_DEFINED_
 
+#include <semanage/context_record.h>
+
 /* User */
 const char *semanage_context_get_user(const semanage_context_t * con)
 {
diff --git a/libsemanage/src/database_join.c b/libsemanage/src/database_join.c
index b9b35a6..a49a622 100644
--- a/libsemanage/src/database_join.c
+++ b/libsemanage/src/database_join.c
@@ -77,10 +77,14 @@
 		goto err;
 
 	/* Sort for quicker merge later */
-	qsort(records1, rcount1, sizeof(record1_t *),
-	      (int (*)(const void *, const void *))rtable1->compare2_qsort);
-	qsort(records2, rcount2, sizeof(record2_t *),
-	      (int (*)(const void *, const void *))rtable2->compare2_qsort);
+	if (rcount1 > 0) {
+		qsort(records1, rcount1, sizeof(record1_t *),
+		      (int (*)(const void *, const void *))rtable1->compare2_qsort);
+	}
+	if (rcount2 > 0) {
+		qsort(records2, rcount2, sizeof(record2_t *),
+		      (int (*)(const void *, const void *))rtable2->compare2_qsort);
+	}
 
 	/* Now merge into this dbase */
 	while (i < rcount1 || j < rcount2) {
diff --git a/libsemanage/src/direct_api.c b/libsemanage/src/direct_api.c
index f0e2300..d83941b 100644
--- a/libsemanage/src/direct_api.c
+++ b/libsemanage/src/direct_api.c
@@ -33,6 +33,8 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
 #include <limits.h>
 #include <errno.h>
 #include <dirent.h>
@@ -50,13 +52,13 @@
 
 #include "debug.h"
 #include "handle.h"
+#include "compressed_file.h"
 #include "modules.h"
 #include "direct_api.h"
 #include "semanage_store.h"
 #include "database_policydb.h"
 #include "policy.h"
-#include <sys/mman.h>
-#include <sys/wait.h>
+#include "sha256.h"
 
 #define PIPE_READ 0
 #define PIPE_WRITE 1
@@ -446,198 +448,10 @@
        return 0;
 }
 
-#include <stdlib.h>
-#include <bzlib.h>
-#include <string.h>
-#include <sys/sendfile.h>
-
-/* bzip() a data to a file, returning the total number of compressed bytes
- * in the file.  Returns -1 if file could not be compressed. */
-static ssize_t bzip(semanage_handle_t *sh, const char *filename, char *data,
-			size_t num_bytes)
-{
-	BZFILE* b;
-	size_t  size = 1<<16;
-	int     bzerror;
-	size_t  total = 0;
-	size_t len = 0;
-	FILE *f;
-
-	if ((f = fopen(filename, "wb")) == NULL) {
-		return -1;
-	}
-
-	if (!sh->conf->bzip_blocksize) {
-		if (fwrite(data, 1, num_bytes, f) < num_bytes) {
-			fclose(f);
-			return -1;
-		}
-		fclose(f);
-		return num_bytes;
-	}
-
-	b = BZ2_bzWriteOpen( &bzerror, f, sh->conf->bzip_blocksize, 0, 0);
-	if (bzerror != BZ_OK) {
-		BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 );
-		return -1;
-	}
-	
-	while ( num_bytes > total ) {
-		if (num_bytes - total > size) {
-			len = size;
-		} else {
-			len = num_bytes - total;
-		}
-		BZ2_bzWrite ( &bzerror, b, &data[total], len );
-		if (bzerror == BZ_IO_ERROR) { 
-			BZ2_bzWriteClose ( &bzerror, b, 1, 0, 0 );
-			return -1;
-		}
-		total += len;
-	}
-
-	BZ2_bzWriteClose ( &bzerror, b, 0, 0, 0 );
-	fclose(f);
-	if (bzerror == BZ_IO_ERROR) {
-		return -1;
-	}
-	return total;
-}
-
-#define BZ2_MAGICSTR "BZh"
-#define BZ2_MAGICLEN (sizeof(BZ2_MAGICSTR)-1)
-
-/* bunzip() a file to '*data', returning the total number of uncompressed bytes
- * in the file.  Returns -1 if file could not be decompressed. */
-ssize_t bunzip(semanage_handle_t *sh, FILE *f, char **data)
-{
-	BZFILE* b = NULL;
-	size_t  nBuf;
-	char*   buf = NULL;
-	size_t  size = 1<<18;
-	size_t  bufsize = size;
-	int     bzerror;
-	size_t  total = 0;
-	char*   uncompress = NULL;
-	char*   tmpalloc = NULL;
-	int     ret = -1;
-
-	buf = malloc(bufsize);
-	if (buf == NULL) {
-		ERR(sh, "Failure allocating memory.");
-		goto exit;
-	}
-
-	/* Check if the file is bzipped */
-	bzerror = fread(buf, 1, BZ2_MAGICLEN, f);
-	rewind(f);
-	if ((bzerror != BZ2_MAGICLEN) || memcmp(buf, BZ2_MAGICSTR, BZ2_MAGICLEN)) {
-		goto exit;
-	}
-
-	b = BZ2_bzReadOpen ( &bzerror, f, 0, sh->conf->bzip_small, NULL, 0 );
-	if ( bzerror != BZ_OK ) {
-		ERR(sh, "Failure opening bz2 archive.");
-		goto exit;
-	}
-
-	uncompress = malloc(size);
-	if (uncompress == NULL) {
-		ERR(sh, "Failure allocating memory.");
-		goto exit;
-	}
-
-	while ( bzerror == BZ_OK) {
-		nBuf = BZ2_bzRead ( &bzerror, b, buf, bufsize);
-		if (( bzerror == BZ_OK ) || ( bzerror == BZ_STREAM_END )) {
-			if (total + nBuf > size) {
-				size *= 2;
-				tmpalloc = realloc(uncompress, size);
-				if (tmpalloc == NULL) {
-					ERR(sh, "Failure allocating memory.");
-					goto exit;
-				}
-				uncompress = tmpalloc;
-			}
-			memcpy(&uncompress[total], buf, nBuf);
-			total += nBuf;
-		}
-	}
-	if ( bzerror != BZ_STREAM_END ) {
-		ERR(sh, "Failure reading bz2 archive.");
-		goto exit;
-	}
-
-	ret = total;
-	*data = uncompress;
-
-exit:
-	BZ2_bzReadClose ( &bzerror, b );
-	free(buf);
-	if ( ret < 0 ) {
-		free(uncompress);
-	}
-	return ret;
-}
-
-/* mmap() a file to '*data',
- *  If the file is bzip compressed map_file will uncompress 
- * the file into '*data'.
- * Returns the total number of bytes in memory .
- * Returns -1 if file could not be opened or mapped. */
-static ssize_t map_file(semanage_handle_t *sh, const char *path, char **data,
-			int *compressed)
-{
-	ssize_t size = -1;
-	char *uncompress;
-	int fd = -1;
-	FILE *file = NULL;
-
-	fd = open(path, O_RDONLY);
-	if (fd == -1) {
-		ERR(sh, "Unable to open %s\n", path);
-		return -1;
-	}
-
-	file = fdopen(fd, "r");
-	if (file == NULL) {
-		ERR(sh, "Unable to open %s\n", path);
-		close(fd);
-		return -1;
-	}
-
-	if ((size = bunzip(sh, file, &uncompress)) > 0) {
-		*data = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
-		if (*data == MAP_FAILED) {
-			free(uncompress);
-			fclose(file);
-			return -1;
-		} else {
-			memcpy(*data, uncompress, size);
-		}
-		free(uncompress);
-		*compressed = 1;
-	} else {
-		struct stat sb;
-		if (fstat(fd, &sb) == -1 ||
-		    (*data = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) ==
-		    MAP_FAILED) {
-			size = -1;
-		} else {
-			size = sb.st_size;
-		}
-		*compressed = 0;
-	} 
-
-	fclose(file);
-
-	return size;
-}
-
 /* Writes a block of data to a file.  Returns 0 on success, -1 on
  * error. */
 static int write_file(semanage_handle_t * sh,
-		      const char *filename, char *data, size_t num_bytes)
+		      const char *filename, const char *data, size_t num_bytes)
 {
 	int out;
 
@@ -1037,23 +851,33 @@
 	return ret;
 }
 
+static void update_checksum_with_len(Sha256Context *context, size_t s)
+{
+	int i;
+	uint8_t buffer[8];
+
+	for (i = 0; i < 8; i++) {
+		buffer[i] = s & 0xff;
+		s >>= 8;
+	}
+	Sha256Update(context, buffer, 8);
+}
+
 static int semanage_compile_module(semanage_handle_t *sh,
-				semanage_module_info_t *modinfo)
+				   semanage_module_info_t *modinfo,
+				   Sha256Context *context)
 {
 	char cil_path[PATH_MAX];
 	char hll_path[PATH_MAX];
 	char *compiler_path = NULL;
 	char *cil_data = NULL;
 	char *err_data = NULL;
-	char *hll_data = NULL;
 	char *start = NULL;
 	char *end = NULL;
-	ssize_t hll_data_len = 0;
-	ssize_t bzip_status;
 	int status = 0;
-	int compressed;
 	size_t cil_data_len = 0;
 	size_t err_data_len = 0;
+	struct file_contents hll_contents = {};
 
 	if (!strcasecmp(modinfo->lang_ext, "cil")) {
 		goto cleanup;
@@ -1084,13 +908,15 @@
 		goto cleanup;
 	}
 
-	if ((hll_data_len = map_file(sh, hll_path, &hll_data, &compressed)) <= 0) {
+	status = map_compressed_file(sh, hll_path, &hll_contents);
+	if (status < 0) {
 		ERR(sh, "Unable to read file %s\n", hll_path);
-		status = -1;
 		goto cleanup;
 	}
 
-	status = semanage_pipe_data(sh, compiler_path, hll_data, (size_t)hll_data_len, &cil_data, &cil_data_len, &err_data, &err_data_len);
+	status = semanage_pipe_data(sh, compiler_path, hll_contents.data,
+				    hll_contents.len, &cil_data, &cil_data_len,
+				    &err_data, &err_data_len);
 	if (err_data_len > 0) {
 		for (start = end = err_data; end < err_data + err_data_len; end++) {
 			if (*end == '\n') {
@@ -1110,10 +936,14 @@
 		goto cleanup;
 	}
 
-	bzip_status = bzip(sh, cil_path, cil_data, cil_data_len);
-	if (bzip_status == -1) {
-		ERR(sh, "Failed to bzip %s\n", cil_path);
-		status = -1;
+	if (context) {
+		update_checksum_with_len(context, cil_data_len);
+		Sha256Update(context, cil_data, cil_data_len);
+	}
+
+	status = write_compressed_file(sh, cil_path, cil_data, cil_data_len);
+	if (status == -1) {
+		ERR(sh, "Failed to write %s\n", cil_path);
 		goto cleanup;
 	}
 
@@ -1131,9 +961,7 @@
 	}
 
 cleanup:
-	if (hll_data_len > 0) {
-		munmap(hll_data, hll_data_len);
-	}
+	unmap_compressed_file(&hll_contents);
 	free(cil_data);
 	free(err_data);
 	free(compiler_path);
@@ -1141,18 +969,40 @@
 	return status;
 }
 
-static int semanage_compile_hll_modules(semanage_handle_t *sh,
-				semanage_module_info_t *modinfos,
-				int num_modinfos)
+static int modinfo_cmp(const void *a, const void *b)
 {
-	int status = 0;
-	int i;
+	const semanage_module_info_t *ma = a;
+	const semanage_module_info_t *mb = b;
+
+	return strcmp(ma->name, mb->name);
+}
+
+static int semanage_compile_hll_modules(semanage_handle_t *sh,
+					semanage_module_info_t *modinfos,
+					int num_modinfos,
+					char *cil_checksum)
+{
+	/* to be incremented when checksum input data format changes */
+	static const size_t CHECKSUM_EPOCH = 1;
+
+	int i, status = 0;
 	char cil_path[PATH_MAX];
 	struct stat sb;
+	Sha256Context context;
+	SHA256_HASH hash;
+	struct file_contents contents = {};
 
 	assert(sh);
 	assert(modinfos);
 
+	/* Sort modules by name to get consistent ordering. */
+	qsort(modinfos, num_modinfos, sizeof(*modinfos), &modinfo_cmp);
+
+	Sha256Initialise(&context);
+	update_checksum_with_len(&context, CHECKSUM_EPOCH);
+
+	/* prefix with module count to avoid collisions */
+	update_checksum_with_len(&context, num_modinfos);
 	for (i = 0; i < num_modinfos; i++) {
 		status = semanage_module_get_path(
 				sh,
@@ -1160,31 +1010,103 @@
 				SEMANAGE_MODULE_PATH_CIL,
 				cil_path,
 				sizeof(cil_path));
-		if (status != 0) {
-			goto cleanup;
+		if (status != 0)
+			return -1;
+
+		if (!semanage_get_ignore_module_cache(sh)) {
+			status = stat(cil_path, &sb);
+			if (status == 0) {
+				status = map_compressed_file(sh, cil_path, &contents);
+				if (status < 0) {
+					ERR(sh, "Error mapping file: %s", cil_path);
+					return -1;
+				}
+
+				/* prefix with length to avoid collisions */
+				update_checksum_with_len(&context, contents.len);
+				Sha256Update(&context, contents.data, contents.len);
+
+				unmap_compressed_file(&contents);
+				continue;
+			} else if (errno != ENOENT) {
+				ERR(sh, "Unable to access %s: %s\n", cil_path,
+				    strerror(errno));
+				return -1; //an error in the "stat" call
+			}
 		}
 
-		if (semanage_get_ignore_module_cache(sh) == 0 &&
-				(status = stat(cil_path, &sb)) == 0) {
-			continue;
-		}
-		if (status != 0 && errno != ENOENT) {
-			ERR(sh, "Unable to access %s: %s\n", cil_path, strerror(errno));
-			goto cleanup; //an error in the "stat" call
-		}
+		status = semanage_compile_module(sh, &modinfos[i], &context);
+		if (status < 0)
+			return -1;
+	}
+	Sha256Finalise(&context, &hash);
 
-		status = semanage_compile_module(sh, &modinfos[i]);
-		if (status < 0) {
-			goto cleanup;
+	semanage_hash_to_checksum_string(hash.bytes, cil_checksum);
+	return 0;
+}
+
+static int semanage_compare_checksum(semanage_handle_t *sh, const char *reference)
+{
+	const char *path = semanage_path(SEMANAGE_TMP, SEMANAGE_MODULES_CHECKSUM);
+	struct stat sb;
+	int fd, retval;
+	char *data;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1) {
+		if (errno != ENOENT) {
+			ERR(sh, "Unable to open %s: %s\n", path, strerror(errno));
+			return -1;
 		}
+		/* Checksum file not present - force a rebuild. */
+		return 1;
 	}
 
-	status = 0;
+	if (fstat(fd, &sb) == -1) {
+		ERR(sh, "Unable to stat %s\n", path);
+		retval = -1;
+		goto out_close;
+	}
 
-cleanup:
-	return status;
+	if (sb.st_size != (off_t)CHECKSUM_CONTENT_SIZE) {
+		/* Incompatible/invalid hash type - just force a rebuild. */
+		WARN(sh, "Module checksum invalid - forcing a rebuild\n");
+		retval = 1;
+		goto out_close;
+	}
+
+	data = mmap(NULL, CHECKSUM_CONTENT_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (data == MAP_FAILED) {
+		ERR(sh, "Unable to mmap %s\n", path);
+		retval = -1;
+		goto out_close;
+	}
+
+	retval = memcmp(data, reference, CHECKSUM_CONTENT_SIZE) != 0;
+	munmap(data, sb.st_size);
+out_close:
+	close(fd);
+	return retval;
 }
 
+static int semanage_write_modules_checksum(semanage_handle_t *sh,
+					   const char *checksum)
+{
+	const char *path = semanage_path(SEMANAGE_TMP, SEMANAGE_MODULES_CHECKSUM);
+
+	return write_file(sh, path, checksum, CHECKSUM_CONTENT_SIZE);
+}
+
+/* Files that must exist in order to skip policy rebuild. */
+static const int semanage_computed_files[] = {
+	SEMANAGE_STORE_KERNEL,
+	SEMANAGE_STORE_FC,
+	SEMANAGE_STORE_SEUSERS,
+	SEMANAGE_LINKED,
+	SEMANAGE_SEUSERS_LINKED,
+	SEMANAGE_USERS_EXTRA_LINKED
+};
+
 /* Copies a file from src to dst. If dst already exists then
  * overwrite it. If source doesn't exist then return success.
  * Returns 0 on success, -1 on error. */
@@ -1211,6 +1133,7 @@
 	semanage_module_info_t *modinfos = NULL;
 	mode_t mask = umask(0077);
 	struct stat sb;
+	char modules_checksum[CHECKSUM_CONTENT_SIZE + 1 /* '\0' */];
 
 	int do_rebuild, do_write_kernel, do_install;
 	int fcontexts_modified, ports_modified, seusers_modified,
@@ -1244,6 +1167,14 @@
 	seusers_modified = seusers->dtable->is_modified(seusers->dbase);
 	fcontexts_modified = fcontexts->dtable->is_modified(fcontexts->dbase);
 
+	/* Before we do anything else, flush the join to its component parts.
+	 * This *does not* flush to disk automatically */
+	if (users->dtable->is_modified(users->dbase)) {
+		retval = users->dtable->flush(sh, users->dbase);
+		if (retval < 0)
+			goto cleanup;
+	}
+
 	/* Rebuild if explicitly requested or any module changes occurred. */
 	do_rebuild = sh->do_rebuild | sh->modules_modified;
 
@@ -1310,14 +1241,6 @@
 		}
 	}
 
-	/* Before we do anything else, flush the join to its component parts.
-	 * This *does not* flush to disk automatically */
-	if (users->dtable->is_modified(users->dbase)) {
-		retval = users->dtable->flush(sh, users->dbase);
-		if (retval < 0)
-			goto cleanup;
-	}
-
 	/*
 	 * This is for systems that have already migrated with an older version
 	 * of semanage_migrate_store. The older version did not copy
@@ -1326,47 +1249,51 @@
 	 * in order to skip re-linking are present; otherwise, we force
 	 * a rebuild.
 	 */
-	if (!do_rebuild) {
-		int files[] = {SEMANAGE_STORE_KERNEL,
-					   SEMANAGE_STORE_FC,
-					   SEMANAGE_STORE_SEUSERS,
-					   SEMANAGE_LINKED,
-					   SEMANAGE_SEUSERS_LINKED,
-					   SEMANAGE_USERS_EXTRA_LINKED};
-
-		for (i = 0; i < (int) ARRAY_SIZE(files); i++) {
-			path = semanage_path(SEMANAGE_TMP, files[i]);
-			if (stat(path, &sb) != 0) {
-				if (errno != ENOENT) {
-					ERR(sh, "Unable to access %s: %s\n", path, strerror(errno));
-					retval = -1;
-					goto cleanup;
-				}
-
-				do_rebuild = 1;
-				goto rebuild;
+	for (i = 0; !do_rebuild && i < (int)ARRAY_SIZE(semanage_computed_files); i++) {
+		path = semanage_path(SEMANAGE_TMP, semanage_computed_files[i]);
+		if (stat(path, &sb) != 0) {
+			if (errno != ENOENT) {
+				ERR(sh, "Unable to access %s: %s\n", path, strerror(errno));
+				retval = -1;
+				goto cleanup;
 			}
+
+			do_rebuild = 1;
+			break;
 		}
 	}
 
-rebuild:
-	/*
-	 * Now that we know whether or not a rebuild is required,
-	 * we can determine what else needs to be done.
-	 * We need to write the kernel policy if we are rebuilding
-	 * or if any other policy component that lives in the kernel
-	 * policy has been modified.
-	 * We need to install the policy files if any of the managed files
-	 * that live under /etc/selinux (kernel policy, seusers, file contexts)
-	 * will be modified.
-	 */
-	do_write_kernel = do_rebuild | ports_modified | ibpkeys_modified |
-		ibendports_modified |
-		bools->dtable->is_modified(bools->dbase) |
-		ifaces->dtable->is_modified(ifaces->dbase) |
-		nodes->dtable->is_modified(nodes->dbase) |
-		users->dtable->is_modified(users_base->dbase);
-	do_install = do_write_kernel | seusers_modified | fcontexts_modified;
+	if (do_rebuild || sh->check_ext_changes) {
+		retval = semanage_get_active_modules(sh, &modinfos, &num_modinfos);
+		if (retval < 0) {
+			goto cleanup;
+		}
+
+		/* No modules - nothing to rebuild. */
+		if (num_modinfos == 0) {
+			goto cleanup;
+		}
+
+		retval = semanage_compile_hll_modules(sh, modinfos, num_modinfos,
+						      modules_checksum);
+		if (retval < 0) {
+			ERR(sh, "Failed to compile hll files into cil files.\n");
+			goto cleanup;
+		}
+
+		if (!do_rebuild && sh->check_ext_changes) {
+			retval = semanage_compare_checksum(sh, modules_checksum);
+			if (retval < 0)
+				goto cleanup;
+			do_rebuild = retval;
+		}
+
+		retval = semanage_write_modules_checksum(sh, modules_checksum);
+		if (retval < 0) {
+			ERR(sh, "Failed to write module checksum file.\n");
+			goto cleanup;
+		}
+	}
 
 	/*
 	 * If there were policy changes, or explicitly requested, or
@@ -1375,21 +1302,6 @@
 	if (do_rebuild) {
 		/* =================== Module expansion =============== */
 
-		retval = semanage_get_active_modules(sh, &modinfos, &num_modinfos);
-		if (retval < 0) {
-			goto cleanup;
-		}
-
-		if (num_modinfos == 0) {
-			goto cleanup;
-		}
-
-		retval = semanage_compile_hll_modules(sh, modinfos, num_modinfos);
-		if (retval < 0) {
-			ERR(sh, "Failed to compile hll files into cil files.\n");
-			goto cleanup;
-		}
-
 		retval = semanage_get_cil_paths(sh, modinfos, num_modinfos, &mod_filenames);
 		if (retval < 0)
 			goto cleanup;
@@ -1521,6 +1433,23 @@
 		}
 	}
 
+	/*
+	 * Determine what else needs to be done.
+	 * We need to write the kernel policy if we are rebuilding
+	 * or if any other policy component that lives in the kernel
+	 * policy has been modified.
+	 * We need to install the policy files if any of the managed files
+	 * that live under /etc/selinux (kernel policy, seusers, file contexts)
+	 * will be modified.
+	 */
+	do_write_kernel = do_rebuild | ports_modified | ibpkeys_modified |
+		ibendports_modified |
+		bools->dtable->is_modified(bools->dbase) |
+		ifaces->dtable->is_modified(ifaces->dbase) |
+		nodes->dtable->is_modified(nodes->dbase) |
+		users->dtable->is_modified(users_base->dbase);
+	do_install = do_write_kernel | seusers_modified | fcontexts_modified;
+
 	/* Attach our databases to the policydb we just created or loaded. */
 	dbase_policydb_attach((dbase_policydb_t *) pusers_base->dbase, out);
 	dbase_policydb_attach((dbase_policydb_t *) pports->dbase, out);
@@ -1756,19 +1685,17 @@
 {
 
 	int retval = -1;
-	char *data = NULL;
-	ssize_t data_len = 0;
-	int compressed = 0;
 	char *path = NULL;
 	char *filename;
 	char *lang_ext = NULL;
 	char *module_name = NULL;
 	char *separator;
 	char *version = NULL;
+	struct file_contents contents = {};
 
-	if ((data_len = map_file(sh, install_filename, &data, &compressed)) <= 0) {
+	retval = map_compressed_file(sh, install_filename, &contents);
+	if (retval < 0) {
 		ERR(sh, "Unable to read file %s\n", install_filename);
-		retval = -1;
 		goto cleanup;
 	}
 
@@ -1781,7 +1708,7 @@
 
 	filename = basename(path);
 
-	if (compressed) {
+	if (contents.compressed) {
 		separator = strrchr(filename, '.');
 		if (separator == NULL) {
 			ERR(sh, "Compressed module does not have a valid extension.");
@@ -1805,7 +1732,8 @@
 	}
 
 	if (strcmp(lang_ext, "pp") == 0) {
-		retval = parse_module_headers(sh, data, data_len, &module_name, &version);
+		retval = parse_module_headers(sh, contents.data, contents.len,
+					      &module_name, &version);
 		free(version);
 		if (retval != 0)
 			goto cleanup;
@@ -1822,10 +1750,11 @@
 		fprintf(stderr, "Warning: SELinux userspace will refer to the module from %s as %s rather than %s\n", install_filename, module_name, filename);
 	}
 
-	retval = semanage_direct_install(sh, data, data_len, module_name, lang_ext);
+	retval = semanage_direct_install(sh, contents.data, contents.len,
+					 module_name, lang_ext);
 
 cleanup:
-	if (data_len > 0) munmap(data, data_len);
+	unmap_compressed_file(&contents);
 	free(module_name);
 	free(path);
 
@@ -1844,10 +1773,8 @@
 	enum semanage_module_path_type file_type;
 	int rc = -1;
 	semanage_module_info_t *_modinfo = NULL;
-	ssize_t _data_len;
-	char *_data;
-	int compressed;
 	struct stat sb;
+	struct file_contents contents = {};
 
 	/* get path of module */
 	rc = semanage_module_get_path(
@@ -1897,25 +1824,39 @@
 			goto cleanup;
 		}
 
-		rc = semanage_compile_module(sh, _modinfo);
+		rc = semanage_compile_module(sh, _modinfo, NULL);
 		if (rc < 0) {
 			goto cleanup;
 		}
 	}
 
-	_data_len = map_file(sh, input_file, &_data, &compressed);
-	if (_data_len <= 0) {
+	rc = map_compressed_file(sh, input_file, &contents);
+	if (rc < 0) {
 		ERR(sh, "Error mapping file: %s", input_file);
-		rc = -1;
 		goto cleanup;
 	}
 
+	/* The API promises an mmap'ed pointer */
+	if (contents.compressed) {
+		*mapped_data = mmap(NULL, contents.len, PROT_READ|PROT_WRITE,
+				    MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
+		if (*mapped_data == MAP_FAILED) {
+			ERR(sh, "Unable to map memory");
+			rc = -1;
+			goto cleanup;
+		}
+		memcpy(*mapped_data, contents.data, contents.len);
+		free(contents.data);
+	} else {
+		*mapped_data = contents.data;
+	}
+
 	*modinfo = _modinfo;
-	*data_len = (size_t)_data_len;
-	*mapped_data = _data;
+	*data_len = contents.len;
 
 cleanup:
 	if (rc != 0) {
+		unmap_compressed_file(&contents);
 		semanage_module_info_destroy(sh, _modinfo);
 		free(_modinfo);
 	}
@@ -2869,8 +2810,8 @@
 		goto cleanup;
 	}
 
-	ret = bzip(sh, path, data, data_len);
-	if (ret <= 0) {
+	ret = write_compressed_file(sh, path, data, data_len);
+	if (ret < 0) {
 		ERR(sh, "Error while writing to %s.", path);
 		status = -3;
 		goto cleanup;
diff --git a/libsemanage/src/direct_api.h b/libsemanage/src/direct_api.h
index e56107b..ffd428e 100644
--- a/libsemanage/src/direct_api.h
+++ b/libsemanage/src/direct_api.h
@@ -39,8 +39,4 @@
 
 int semanage_direct_mls_enabled(struct semanage_handle *sh);
 
-#include <stdio.h>
-#include <unistd.h>
-ssize_t bunzip(struct semanage_handle *sh, FILE *f, char **data);
-
 #endif
diff --git a/libsemanage/src/fcontexts_file.c b/libsemanage/src/fcontexts_file.c
index 04cd365..f357941 100644
--- a/libsemanage/src/fcontexts_file.c
+++ b/libsemanage/src/fcontexts_file.c
@@ -90,7 +90,7 @@
 		goto last;
 
 	/* Regexp */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_fcontext_set_expr(handle, fcontext, str) < 0)
 		goto err;
@@ -100,7 +100,7 @@
 	/* Type */
 	if (parse_assert_space(handle, info) < 0)
 		goto err;
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (!strcasecmp(str, "-s"))
 		semanage_fcontext_set_type(fcontext, SEMANAGE_FCONTEXT_SOCK);
@@ -124,7 +124,7 @@
 	/* Context */
 	if (parse_assert_space(handle, info) < 0)
 		goto err;
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 
       process_context:
diff --git a/libsemanage/src/handle.c b/libsemanage/src/handle.c
index bb1e614..b2201ee 100644
--- a/libsemanage/src/handle.c
+++ b/libsemanage/src/handle.c
@@ -116,20 +116,23 @@
 
 void semanage_set_rebuild(semanage_handle_t * sh, int do_rebuild)
 {
-
 	assert(sh != NULL);
 
 	sh->do_rebuild = do_rebuild;
-	return;
 }
 
 void semanage_set_reload(semanage_handle_t * sh, int do_reload)
 {
-
 	assert(sh != NULL);
 
 	sh->do_reload = do_reload;
-	return;
+}
+
+void semanage_set_check_ext_changes(semanage_handle_t * sh, int do_check)
+{
+	assert(sh != NULL);
+
+	sh->check_ext_changes = do_check;
 }
 
 int semanage_get_hll_compiler_path(semanage_handle_t *sh,
diff --git a/libsemanage/src/handle.h b/libsemanage/src/handle.h
index e1ce83b..4d2aae8 100644
--- a/libsemanage/src/handle.h
+++ b/libsemanage/src/handle.h
@@ -61,6 +61,7 @@
 	int is_in_transaction;
 	int do_reload;		/* whether to reload policy after commit */
 	int do_rebuild;		/* whether to rebuild policy if there were no changes */
+	int check_ext_changes;	/* whether to rebuild if external changes are detected via checksum */
 	int commit_err;		/* set by semanage_direct_commit() if there are
 				 * any errors when building or committing the
 				 * sandbox to kernel policy at /etc/selinux
diff --git a/libsemanage/src/ibendports_file.c b/libsemanage/src/ibendports_file.c
index bafa8c1..2fa2a67 100644
--- a/libsemanage/src/ibendports_file.c
+++ b/libsemanage/src/ibendports_file.c
@@ -75,7 +75,7 @@
 		goto err;
 
 	/* IB Device Name */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_ibendport_set_ibdev_name(handle, ibendport, str) < 0)
 		goto err;
@@ -92,7 +92,7 @@
 	/* context */
 	if (parse_assert_space(handle, info) < 0)
 		goto err;
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_context_from_string(handle, str, &con) < 0) {
 		ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s",
diff --git a/libsemanage/src/ibpkeys_file.c b/libsemanage/src/ibpkeys_file.c
index 929bc31..edde69f 100644
--- a/libsemanage/src/ibpkeys_file.c
+++ b/libsemanage/src/ibpkeys_file.c
@@ -80,7 +80,7 @@
 		goto err;
 
 	/* Subnet Prefix */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_ibpkey_set_subnet_prefix(handle, ibpkey, str) < 0)
 		goto err;
@@ -115,7 +115,7 @@
 		semanage_ibpkey_set_pkey(ibpkey, low);
 	}
 	/* Pkey context */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_context_from_string(handle, str, &con) < 0) {
 		ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s",
diff --git a/libsemanage/src/interfaces_file.c b/libsemanage/src/interfaces_file.c
index c19c8f9..244f0ae 100644
--- a/libsemanage/src/interfaces_file.c
+++ b/libsemanage/src/interfaces_file.c
@@ -72,7 +72,7 @@
 		goto err;
 
 	/* Name */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_iface_set_name(handle, iface, str) < 0)
 		goto err;
@@ -82,7 +82,7 @@
 	/* Interface context */
 	if (parse_assert_space(handle, info) < 0)
 		goto err;
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_context_from_string(handle, str, &con) < 0) {
 		ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s",
@@ -106,7 +106,7 @@
 	/* Message context */
 	if (parse_assert_space(handle, info) < 0)
 		goto err;
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_context_from_string(handle, str, &con) < 0) {
 		ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s",
diff --git a/libsemanage/src/libsemanage.map b/libsemanage/src/libsemanage.map
index 3ea7b60..c8214b2 100644
--- a/libsemanage/src/libsemanage.map
+++ b/libsemanage/src/libsemanage.map
@@ -345,3 +345,8 @@
     semanage_module_remove_key;
     semanage_set_store_root;
 } LIBSEMANAGE_1.0;
+
+LIBSEMANAGE_3.4 {
+    semanage_module_compute_checksum;
+    semanage_set_check_ext_changes;
+} LIBSEMANAGE_1.1;
diff --git a/libsemanage/src/modules.c b/libsemanage/src/modules.c
index b6dd456..c3bd90a 100644
--- a/libsemanage/src/modules.c
+++ b/libsemanage/src/modules.c
@@ -35,11 +35,13 @@
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
 #include <errno.h>
 #include <ctype.h>
 
 #include "handle.h"
 #include "modules.h"
+#include "sha256.h"
 #include "debug.h"
 
 int semanage_module_install(semanage_handle_t * sh,
@@ -168,6 +170,7 @@
 /* Legacy function that remains to preserve ABI
  * compatibility.
  */
+extern const char *semanage_module_get_version(semanage_module_info_t *);
 const char *semanage_module_get_version(semanage_module_info_t * modinfo
 				__attribute__ ((unused)))
 {
@@ -975,3 +978,60 @@
 	return sh->funcs->remove_key(sh, modkey);
 }
 
+static const char CHECKSUM_TYPE[] = "sha256";
+const size_t CHECKSUM_CONTENT_SIZE = sizeof(CHECKSUM_TYPE) + 1 + 2 * SHA256_HASH_SIZE;
+
+void semanage_hash_to_checksum_string(const uint8_t *hash, char *checksum)
+{
+	size_t i;
+
+	checksum += sprintf(checksum, "%s:", CHECKSUM_TYPE);
+	for (i = 0; i < SHA256_HASH_SIZE; i++) {
+		checksum += sprintf(checksum, "%02x", (unsigned)hash[i]);
+	}
+}
+
+int semanage_module_compute_checksum(semanage_handle_t *sh,
+				     semanage_module_key_t *modkey,
+				     int cil, char **checksum,
+				     size_t *checksum_len)
+{
+	semanage_module_info_t *extract_info = NULL;
+	Sha256Context context;
+	SHA256_HASH sha256_hash;
+	char *checksum_str;
+	void *data;
+	size_t data_len = 0;
+	int result;
+
+	if (!checksum_len)
+		return -1;
+
+	if (!checksum) {
+		*checksum_len = CHECKSUM_CONTENT_SIZE;
+		return 0;
+	}
+
+	result = semanage_module_extract(sh, modkey, cil, &data, &data_len, &extract_info);
+	if (result != 0)
+		return -1;
+
+	semanage_module_info_destroy(sh, extract_info);
+	free(extract_info);
+
+	Sha256Initialise(&context);
+	Sha256Update(&context, data, data_len);
+	Sha256Finalise(&context, &sha256_hash);
+
+	munmap(data, data_len);
+
+	checksum_str = malloc(CHECKSUM_CONTENT_SIZE + 1 /* '\0' */);
+	if (!checksum_str)
+		return -1;
+
+	semanage_hash_to_checksum_string(sha256_hash.bytes, checksum_str);
+
+	*checksum = checksum_str;
+	*checksum_len = CHECKSUM_CONTENT_SIZE;
+	return 0;
+}
diff --git a/libsemanage/src/modules.h b/libsemanage/src/modules.h
index 64d4a15..cf2432c 100644
--- a/libsemanage/src/modules.h
+++ b/libsemanage/src/modules.h
@@ -102,4 +102,7 @@
 			     char *path,
 			     size_t len);
 
+extern const size_t CHECKSUM_CONTENT_SIZE;
+void semanage_hash_to_checksum_string(const uint8_t *hash, char *checksum);
+
 #endif
diff --git a/libsemanage/src/nodes_file.c b/libsemanage/src/nodes_file.c
index c3647f2..2d2b7fe 100644
--- a/libsemanage/src/nodes_file.c
+++ b/libsemanage/src/nodes_file.c
@@ -77,7 +77,7 @@
 		goto err;
 
 	/* Protocol */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (!strcasecmp(str, "ipv4"))
 		proto = SEMANAGE_PROTO_IP4;
@@ -96,7 +96,7 @@
 	/* Address */
 	if (parse_assert_space(handle, info) < 0)
 		goto err;
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_node_set_addr(handle, node, proto, str) < 0)
 		goto err;
@@ -106,7 +106,7 @@
 	str = NULL;
 
 	/* Netmask */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_node_set_mask(handle, node, proto, str) < 0)
 		goto err;
@@ -116,7 +116,7 @@
 	str = NULL;
 
 	/* Port context */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_context_from_string(handle, str, &con) < 0) {
 		ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s",
diff --git a/libsemanage/src/parse_utils.c b/libsemanage/src/parse_utils.c
index 4fb54fc..918dee4 100644
--- a/libsemanage/src/parse_utils.c
+++ b/libsemanage/src/parse_utils.c
@@ -239,7 +239,7 @@
 	char *test = NULL;
 	int value = 0;
 
-	if (parse_fetch_string(handle, info, &str, delim) < 0)
+	if (parse_fetch_string(handle, info, &str, delim, 0) < 0)
 		goto err;
 
 	if (!isdigit((int)*str)) {
@@ -267,7 +267,7 @@
 }
 
 int parse_fetch_string(semanage_handle_t * handle,
-		       parse_info_t * info, char **str, char delim)
+		       parse_info_t * info, char **str, char delim, int allow_spaces)
 {
 
 	char *start = info->ptr;
@@ -277,7 +277,7 @@
 	if (parse_assert_noeof(handle, info) < 0)
 		goto err;
 
-	while (*(info->ptr) && !isspace(*(info->ptr)) &&
+	while (*(info->ptr) && (allow_spaces || !isspace(*(info->ptr))) &&
 	       (*(info->ptr) != delim)) {
 		info->ptr++;
 		len++;
diff --git a/libsemanage/src/parse_utils.h b/libsemanage/src/parse_utils.h
index 0f33486..3e44aca 100644
--- a/libsemanage/src/parse_utils.h
+++ b/libsemanage/src/parse_utils.h
@@ -71,12 +71,11 @@
 int parse_fetch_int(semanage_handle_t * hgandle,
 		    parse_info_t * info, int *num, char delim);
 
-/* Extract the next string (delimited by 
- * whitespace), and move the read pointer past it.
- * Stop of the optional character delim is encountered,
- * or if whitespace/eof is encountered. Fail if the
- * string is of length 0. */
+/* Extract the next string and move the read pointer past it.
+ * Stop if the optional character delim (or eof) is encountered,
+ * or if whitespace is encountered and allow_spaces is 0.
+ * Fail if the string is of length 0. */
 extern int parse_fetch_string(semanage_handle_t * handle,
-			      parse_info_t * info, char **str_ptr, char delim);
+			      parse_info_t * info, char **str_ptr, char delim, int allow_spaces);
 
 #endif
diff --git a/libsemanage/src/ports_file.c b/libsemanage/src/ports_file.c
index ade4102..1356021 100644
--- a/libsemanage/src/ports_file.c
+++ b/libsemanage/src/ports_file.c
@@ -77,7 +77,7 @@
 		goto err;
 
 	/* Protocol */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (!strcasecmp(str, "tcp"))
 		semanage_port_set_proto(port, SEMANAGE_PROTO_TCP);
@@ -123,7 +123,7 @@
 		semanage_port_set_port(port, low);
 
 	/* Port context */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_context_from_string(handle, str, &con) < 0) {
 		ERR(handle, "invalid security context \"%s\" (%s: %u)\n%s",
diff --git a/libsemanage/src/semanage.conf b/libsemanage/src/semanage.conf
index dc8d46b..98d769b 100644
--- a/libsemanage/src/semanage.conf
+++ b/libsemanage/src/semanage.conf
@@ -24,8 +24,9 @@
 #  /foo/bar     - Write by way of a policy management server, whose
 #                 named socket is at /foo/bar.  The path must begin
 #                 with a '/'.
-#  foo.com:4242 - Establish a TCP connection to a remote policy
-#                 management server at foo.com.  If there is a colon
+#  example.com:4242
+#               - Establish a TCP connection to a remote policy
+#                 management server at example.com.  If there is a colon
 #                 then the remainder is interpreted as a port number;
 #                 otherwise default to port 4242.
 module-store = direct
diff --git a/libsemanage/src/semanage_store.c b/libsemanage/src/semanage_store.c
index c6a736f..767f05c 100644
--- a/libsemanage/src/semanage_store.c
+++ b/libsemanage/src/semanage_store.c
@@ -59,6 +59,7 @@
 
 #include "debug.h"
 #include "utilities.h"
+#include "compressed_file.h"
 
 #define SEMANAGE_CONF_FILE "semanage.conf"
 /* relative path names to enum semanage_paths to special files and
@@ -114,6 +115,7 @@
 	"/disable_dontaudit",
 	"/preserve_tunables",
 	"/modules/disabled",
+	"/modules_checksum",
 	"/policy.kern",
 	"/file_contexts.local",
 	"/file_contexts.homedirs",
@@ -2054,60 +2056,27 @@
 
 int semanage_load_files(semanage_handle_t * sh, cil_db_t *cildb, char **filenames, int numfiles)
 {
-	int retval = 0;
-	FILE *fp;
-	ssize_t size;
-	char *data = NULL;
+	int i, retval = 0;
 	char *filename;
-	int i;
+	struct file_contents contents = {};
 
 	for (i = 0; i < numfiles; i++) {
 		filename = filenames[i];
 
-		if ((fp = fopen(filename, "rb")) == NULL) {
-			ERR(sh, "Could not open module file %s for reading.", filename);
-			goto cleanup;
-		}
+		retval = map_compressed_file(sh, filename, &contents);
+		if (retval < 0)
+			return -1;
 
-		if ((size = bunzip(sh, fp, &data)) <= 0) {
-			rewind(fp);
-			__fsetlocking(fp, FSETLOCKING_BYCALLER);
+		retval = cil_add_file(cildb, filename, contents.data, contents.len);
+		unmap_compressed_file(&contents);
 
-			if (fseek(fp, 0, SEEK_END) != 0) {
-				ERR(sh, "Failed to determine size of file %s.", filename);
-				goto cleanup;
-			}
-			size = ftell(fp);
-			rewind(fp);
-
-			data = malloc(size);
-			if (fread(data, size, 1, fp) != 1) {
-				ERR(sh, "Failed to read file %s.", filename);
-				goto cleanup;
-			}
-		}
-
-		fclose(fp);
-		fp = NULL;
-
-		retval = cil_add_file(cildb, filename, data, size);
 		if (retval != SEPOL_OK) {
 			ERR(sh, "Error while reading from file %s.", filename);
-			goto cleanup;
+			return -1;
 		}
-	
-		free(data);
-		data = NULL;
 	}
 
-	return retval;
-
-      cleanup:
-	if (fp != NULL) {
-		fclose(fp);
-	}
-	free(data);
-	return -1;
+	return 0;
 }
 
 /* 
diff --git a/libsemanage/src/semanage_store.h b/libsemanage/src/semanage_store.h
index b9ec566..1fc77da 100644
--- a/libsemanage/src/semanage_store.h
+++ b/libsemanage/src/semanage_store.h
@@ -60,6 +60,7 @@
 	SEMANAGE_DISABLE_DONTAUDIT,
 	SEMANAGE_PRESERVE_TUNABLES,
 	SEMANAGE_MODULES_DISABLED,
+	SEMANAGE_MODULES_CHECKSUM,
 	SEMANAGE_STORE_KERNEL,
 	SEMANAGE_STORE_FC_LOCAL,
 	SEMANAGE_STORE_FC_HOMEDIRS,
diff --git a/libsemanage/src/semanageswig_python_exception.i b/libsemanage/src/semanageswig_python_exception.i
index 372ec94..0df8bbc 100644
--- a/libsemanage/src/semanageswig_python_exception.i
+++ b/libsemanage/src/semanageswig_python_exception.i
@@ -351,6 +351,14 @@
   }
 }
 
+%exception semanage_module_compute_checksum {
+  $action
+  if (result < 0) {
+     PyErr_SetFromErrno(PyExc_OSError);
+     SWIG_fail;
+  }
+}
+
 %exception semanage_msg_get_level {
   $action
   if (result < 0) {
diff --git a/libsemanage/src/seusers_file.c b/libsemanage/src/seusers_file.c
index 910bedf..21b970a 100644
--- a/libsemanage/src/seusers_file.c
+++ b/libsemanage/src/seusers_file.c
@@ -53,7 +53,7 @@
 		goto last;
 
 	/* Extract name */
-	if (parse_fetch_string(handle, info, &str, ':') < 0)
+	if (parse_fetch_string(handle, info, &str, ':', 1) < 0)
 		goto err;
 	if (semanage_seuser_set_name(handle, seuser, str) < 0)
 		goto err;
@@ -68,7 +68,7 @@
 		goto err;
 
 	/* Extract sename */
-	if (parse_fetch_string(handle, info, &str, ':') < 0)
+	if (parse_fetch_string(handle, info, &str, ':', 1) < 0)
 		goto err;
 	if (semanage_seuser_set_sename(handle, seuser, str) < 0)
 		goto err;
@@ -83,7 +83,7 @@
 		goto err;
 
 	/* NOTE: does not allow spaces/multiline */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 
 	if (semanage_seuser_set_mlsrange(handle, seuser, str) < 0)
diff --git a/libsemanage/src/sha256.c b/libsemanage/src/sha256.c
new file mode 100644
index 0000000..fe2aeef
--- /dev/null
+++ b/libsemanage/src/sha256.c
@@ -0,0 +1,294 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  WjCryptLib_Sha256
+//
+//  Implementation of SHA256 hash function.
+//  Original author: Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+//  Modified by WaterJuice retaining Public Domain license.
+//
+//  This is free and unencumbered software released into the public domain - June 2013 waterjuice.org
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  IMPORTS
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "sha256.h"
+#include <memory.h>
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  MACROS
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#define ror(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits))))
+
+#define MIN(x, y) ( ((x)<(y))?(x):(y) )
+
+#define STORE32H(x, y)                                                                     \
+     { (y)[0] = (uint8_t)(((x)>>24)&255); (y)[1] = (uint8_t)(((x)>>16)&255);   \
+       (y)[2] = (uint8_t)(((x)>>8)&255); (y)[3] = (uint8_t)((x)&255); }
+
+#define LOAD32H(x, y)                            \
+     { x = ((uint32_t)((y)[0] & 255)<<24) | \
+           ((uint32_t)((y)[1] & 255)<<16) | \
+           ((uint32_t)((y)[2] & 255)<<8)  | \
+           ((uint32_t)((y)[3] & 255)); }
+
+#define STORE64H(x, y)                                                                     \
+   { (y)[0] = (uint8_t)(((x)>>56)&255); (y)[1] = (uint8_t)(((x)>>48)&255);     \
+     (y)[2] = (uint8_t)(((x)>>40)&255); (y)[3] = (uint8_t)(((x)>>32)&255);     \
+     (y)[4] = (uint8_t)(((x)>>24)&255); (y)[5] = (uint8_t)(((x)>>16)&255);     \
+     (y)[6] = (uint8_t)(((x)>>8)&255); (y)[7] = (uint8_t)((x)&255); }
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  CONSTANTS
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// The K array
+static const uint32_t K[64] = {
+    0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+    0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+    0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+    0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+    0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+    0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+    0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+    0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+    0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+    0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+    0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+    0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+    0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+#define BLOCK_SIZE          64
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  INTERNAL FUNCTIONS
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Various logical functions
+#define Ch( x, y, z )     (z ^ (x & (y ^ z)))
+#define Maj( x, y, z )    (((x | y) & z) | (x & y))
+#define S( x, n )         ror((x),(n))
+#define R( x, n )         (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0( x )       (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1( x )       (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0( x )       (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1( x )       (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+
+#define Sha256Round( a, b, c, d, e, f, g, h, i )       \
+     t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];   \
+     t1 = Sigma0(a) + Maj(a, b, c);                    \
+     d += t0;                                          \
+     h  = t0 + t1;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  TransformFunction
+//
+//  Compress 512-bits
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static
+void
+    TransformFunction
+    (
+        Sha256Context*      Context,
+        uint8_t const*      Buffer
+    )
+{
+    uint32_t    S[8];
+    uint32_t    W[64];
+    uint32_t    t0;
+    uint32_t    t1;
+    uint32_t    t;
+    int         i;
+
+    // Copy state into S
+    for( i=0; i<8; i++ )
+    {
+        S[i] = Context->state[i];
+    }
+
+    // Copy the state into 512-bits into W[0..15]
+    for( i=0; i<16; i++ )
+    {
+        LOAD32H( W[i], Buffer + (4*i) );
+    }
+
+    // Fill W[16..63]
+    for( i=16; i<64; i++ )
+    {
+        W[i] = Gamma1( W[i-2]) + W[i-7] + Gamma0( W[i-15] ) + W[i-16];
+    }
+
+    // Compress
+    for( i=0; i<64; i++ )
+    {
+        Sha256Round( S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i );
+        t = S[7];
+        S[7] = S[6];
+        S[6] = S[5];
+        S[5] = S[4];
+        S[4] = S[3];
+        S[3] = S[2];
+        S[2] = S[1];
+        S[1] = S[0];
+        S[0] = t;
+    }
+
+    // Feedback
+    for( i=0; i<8; i++ )
+    {
+        Context->state[i] = Context->state[i] + S[i];
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  PUBLIC FUNCTIONS
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  Sha256Initialise
+//
+//  Initialises a SHA256 Context. Use this to initialise/reset a context.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void
+    Sha256Initialise
+    (
+        Sha256Context*      Context         // [out]
+    )
+{
+    Context->curlen = 0;
+    Context->length = 0;
+    Context->state[0] = 0x6A09E667UL;
+    Context->state[1] = 0xBB67AE85UL;
+    Context->state[2] = 0x3C6EF372UL;
+    Context->state[3] = 0xA54FF53AUL;
+    Context->state[4] = 0x510E527FUL;
+    Context->state[5] = 0x9B05688CUL;
+    Context->state[6] = 0x1F83D9ABUL;
+    Context->state[7] = 0x5BE0CD19UL;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  Sha256Update
+//
+//  Adds data to the SHA256 context. This will process the data and update the internal state of the context. Keep on
+//  calling this function until all the data has been added. Then call Sha256Finalise to calculate the hash.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void
+    Sha256Update
+    (
+        Sha256Context*      Context,        // [in out]
+        void const*         Buffer,         // [in]
+        uint32_t            BufferSize      // [in]
+    )
+{
+    uint32_t n;
+
+    if( Context->curlen > sizeof(Context->buf) )
+    {
+       return;
+    }
+
+    while( BufferSize > 0 )
+    {
+        if( Context->curlen == 0 && BufferSize >= BLOCK_SIZE )
+        {
+           TransformFunction( Context, (uint8_t*)Buffer );
+           Context->length += BLOCK_SIZE * 8;
+           Buffer = (uint8_t*)Buffer + BLOCK_SIZE;
+           BufferSize -= BLOCK_SIZE;
+        }
+        else
+        {
+           n = MIN( BufferSize, (BLOCK_SIZE - Context->curlen) );
+           memcpy( Context->buf + Context->curlen, Buffer, (size_t)n );
+           Context->curlen += n;
+           Buffer = (uint8_t*)Buffer + n;
+           BufferSize -= n;
+           if( Context->curlen == BLOCK_SIZE )
+           {
+              TransformFunction( Context, Context->buf );
+              Context->length += 8*BLOCK_SIZE;
+              Context->curlen = 0;
+           }
+       }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  Sha256Finalise
+//
+//  Performs the final calculation of the hash and returns the digest (32 byte buffer containing 256bit hash). After
+//  calling this, Sha256Initialised must be used to reuse the context.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void
+    Sha256Finalise
+    (
+        Sha256Context*      Context,        // [in out]
+        SHA256_HASH*        Digest          // [out]
+    )
+{
+    int i;
+
+    if( Context->curlen >= sizeof(Context->buf) )
+    {
+       return;
+    }
+
+    // Increase the length of the message
+    Context->length += Context->curlen * 8;
+
+    // Append the '1' bit
+    Context->buf[Context->curlen++] = (uint8_t)0x80;
+
+    // if the length is currently above 56 bytes we append zeros
+    // then compress.  Then we can fall back to padding zeros and length
+    // encoding like normal.
+    if( Context->curlen > 56 )
+    {
+        while( Context->curlen < 64 )
+        {
+            Context->buf[Context->curlen++] = (uint8_t)0;
+        }
+        TransformFunction(Context, Context->buf);
+        Context->curlen = 0;
+    }
+
+    // Pad up to 56 bytes of zeroes
+    while( Context->curlen < 56 )
+    {
+        Context->buf[Context->curlen++] = (uint8_t)0;
+    }
+
+    // Store length
+    STORE64H( Context->length, Context->buf+56 );
+    TransformFunction( Context, Context->buf );
+
+    // Copy output
+    for( i=0; i<8; i++ )
+    {
+        STORE32H( Context->state[i], Digest->bytes+(4*i) );
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  Sha256Calculate
+//
+//  Combines Sha256Initialise, Sha256Update, and Sha256Finalise into one function. Calculates the SHA256 hash of the
+//  buffer.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void
+    Sha256Calculate
+    (
+        void  const*        Buffer,         // [in]
+        uint32_t            BufferSize,     // [in]
+        SHA256_HASH*        Digest          // [in]
+    )
+{
+    Sha256Context context;
+
+    Sha256Initialise( &context );
+    Sha256Update( &context, Buffer, BufferSize );
+    Sha256Finalise( &context, Digest );
+}
diff --git a/libsemanage/src/sha256.h b/libsemanage/src/sha256.h
new file mode 100644
index 0000000..406ed86
--- /dev/null
+++ b/libsemanage/src/sha256.h
@@ -0,0 +1,89 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  WjCryptLib_Sha256
+//
+//  Implementation of SHA256 hash function.
+//  Original author: Tom St Denis, tomstdenis@gmail.com, http://libtom.org
+//  Modified by WaterJuice retaining Public Domain license.
+//
+//  This is free and unencumbered software released into the public domain - June 2013 waterjuice.org
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  IMPORTS
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct
+{
+    uint64_t    length;
+    uint32_t    state[8];
+    uint32_t    curlen;
+    uint8_t     buf[64];
+} Sha256Context;
+
+#define SHA256_HASH_SIZE           ( 256 / 8 )
+
+typedef struct
+{
+    uint8_t      bytes [SHA256_HASH_SIZE];
+} SHA256_HASH;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  PUBLIC FUNCTIONS
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  Sha256Initialise
+//
+//  Initialises a SHA256 Context. Use this to initialise/reset a context.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void
+    Sha256Initialise
+    (
+        Sha256Context*      Context         // [out]
+    );
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  Sha256Update
+//
+//  Adds data to the SHA256 context. This will process the data and update the internal state of the context. Keep on
+//  calling this function until all the data has been added. Then call Sha256Finalise to calculate the hash.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void
+    Sha256Update
+    (
+        Sha256Context*      Context,        // [in out]
+        void const*         Buffer,         // [in]
+        uint32_t            BufferSize      // [in]
+    );
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  Sha256Finalise
+//
+//  Performs the final calculation of the hash and returns the digest (32 byte buffer containing 256bit hash). After
+//  calling this, Sha256Initialised must be used to reuse the context.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void
+    Sha256Finalise
+    (
+        Sha256Context*      Context,        // [in out]
+        SHA256_HASH*        Digest          // [out]
+    );
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//  Sha256Calculate
+//
+//  Combines Sha256Initialise, Sha256Update, and Sha256Finalise into one function. Calculates the SHA256 hash of the
+//  buffer.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void
+    Sha256Calculate
+    (
+        void  const*        Buffer,         // [in]
+        uint32_t            BufferSize,     // [in]
+        SHA256_HASH*        Digest          // [in]
+    );
diff --git a/libsemanage/src/users_base_file.c b/libsemanage/src/users_base_file.c
index 0f0a8fd..a0f8cd7 100644
--- a/libsemanage/src/users_base_file.c
+++ b/libsemanage/src/users_base_file.c
@@ -83,7 +83,7 @@
 		goto err;
 
 	/* Parse user name */
-	if (parse_fetch_string(handle, info, &name_str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &name_str, ' ', 0) < 0)
 		goto err;
 
 	if (semanage_user_base_set_name(handle, user, name_str) < 0) {
@@ -150,7 +150,7 @@
 		goto err;
 
 	/* NOTE: does not allow spaces/multiline */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_user_base_set_mlslevel(handle, user, str) < 0)
 		goto err;
@@ -165,8 +165,7 @@
 	if (parse_assert_space(handle, info) < 0)
 		goto err;
 
-	/* NOTE: does not allow spaces/multiline */
-	if (parse_fetch_string(handle, info, &str, ';') < 0)
+	if (parse_fetch_string(handle, info, &str, ';', 1) < 0)
 		goto err;
 	if (semanage_user_base_set_mlsrange(handle, user, str) < 0)
 		goto err;
diff --git a/libsemanage/src/users_extra_file.c b/libsemanage/src/users_extra_file.c
index 8f2bebd..7aa9df3 100644
--- a/libsemanage/src/users_extra_file.c
+++ b/libsemanage/src/users_extra_file.c
@@ -57,7 +57,7 @@
 		goto err;
 
 	/* Extract name */
-	if (parse_fetch_string(handle, info, &str, ' ') < 0)
+	if (parse_fetch_string(handle, info, &str, ' ', 0) < 0)
 		goto err;
 	if (semanage_user_extra_set_name(handle, user_extra, str) < 0)
 		goto err;
@@ -73,7 +73,7 @@
 		goto err;
 
 	/* Extract prefix */
-	if (parse_fetch_string(handle, info, &str, ';') < 0)
+	if (parse_fetch_string(handle, info, &str, ';', 1) < 0)
 		goto err;
 	if (semanage_user_extra_set_prefix(handle, user_extra, str) < 0)
 		goto err;
diff --git a/libsemanage/src/utilities.c b/libsemanage/src/utilities.c
index fc5a6a5..fdbb8ad 100644
--- a/libsemanage/src/utilities.c
+++ b/libsemanage/src/utilities.c
@@ -292,7 +292,7 @@
  *
  * returns the newly created node or NULL on error
  */
-semanage_list_t *list_addafter_controlmem(semanage_list_t * item, char *data)
+static semanage_list_t *list_addafter_controlmem(semanage_list_t * item, char *data)
 {
 	semanage_list_t *temp = malloc(sizeof(semanage_list_t));
 
diff --git a/libsemanage/tests/test_bool.c b/libsemanage/tests/test_bool.c
index ae80d44..7bf5225 100644
--- a/libsemanage/tests/test_bool.c
+++ b/libsemanage/tests/test_bool.c
@@ -132,6 +132,8 @@
 		if (i != (unsigned int) idx)
 			semanage_bool_free(records[i]);
 
+	free(records);
+
 	return boolean;
 }
 
@@ -163,6 +165,8 @@
 	CU_ASSERT_FATAL(res >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
+	semanage_bool_free(boolean);
+
 	return key;
 }
 
@@ -196,6 +200,9 @@
 	CU_ASSERT_PTR_NOT_NULL_FATAL(boolean);
 
 	CU_ASSERT_FATAL(semanage_bool_modify_local(sh, key, boolean) >= 0);
+
+	semanage_bool_key_free(key);
+	semanage_bool_free(boolean);
 }
 
 void delete_local_bool(const char *name)
@@ -208,6 +215,8 @@
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
 	CU_ASSERT_FATAL(semanage_bool_del_local(sh, key) >= 0);
+
+	semanage_bool_key_free(key);
 }
 
 /* Function bool_key_create */
@@ -447,6 +456,8 @@
 	CU_ASSERT_PTR_NULL(semanage_bool_get_name(boolean));
 	CU_ASSERT(semanage_bool_get_value(boolean) == 0);
 
+	semanage_bool_free(boolean);
+
 	cleanup_handle(level);
 }
 
@@ -483,6 +494,9 @@
 
 	CU_ASSERT_EQUAL(val, val_clone);
 
+	semanage_bool_free(boolean_clone);
+	semanage_bool_free(boolean);
+
 	cleanup_handle(level);
 }
 
@@ -514,6 +528,9 @@
 		CU_ASSERT_PTR_NULL(resp);
 	}
 
+	semanage_bool_free(resp);
+	semanage_bool_key_free(key);
+
 	cleanup_handle(level);
 }
 
@@ -647,6 +664,8 @@
 	for (unsigned int i = 0; i < count; i++)
 		semanage_bool_free(records[i]);
 
+	free(records);
+
 	cleanup_handle(level);
 }
 
@@ -662,7 +681,7 @@
 				  int old_val, int exp_res)
 {
 	semanage_bool_t *boolean;
-	semanage_bool_t *boolean_local;
+	semanage_bool_t *boolean_local = NULL;
 	semanage_bool_key_t *key = NULL;
 	int res;
 	int new_val;
@@ -696,6 +715,8 @@
 		CU_ASSERT(semanage_bool_query_local(sh, key,
 					            &boolean_local) >= 0);
 		CU_ASSERT(semanage_bool_compare2(boolean_local, boolean) == 0);
+		semanage_bool_free(boolean_local);
+
 		CU_ASSERT(semanage_bool_del_local(sh, key) >= 0);
 		CU_ASSERT(semanage_bool_query_local(sh, key,
 						    &boolean_local) < 0);
@@ -734,15 +755,18 @@
 
 	/* transaction */
 	setup_handle(SH_TRANS);
+	semanage_bool_key_free(key);
 	CU_ASSERT(semanage_bool_key_create(sh, BOOL1_NAME, &key) >= 0);
 	CU_ASSERT_PTR_NOT_NULL(key);
 
 	CU_ASSERT(semanage_bool_query_local(sh, key, &resp) < 0);
 	CU_ASSERT_PTR_NULL(resp);
+	semanage_bool_free(resp);
 
 	add_local_bool(BOOL1_NAME);
 	CU_ASSERT(semanage_bool_query_local(sh, key, &resp) >= 0);
 	CU_ASSERT_PTR_NOT_NULL(resp);
+	semanage_bool_free(resp);
 
 	semanage_bool_key_free(key);
 	CU_ASSERT(semanage_bool_key_create(sh, BOOL2_NAME, &key) >= 0);
@@ -751,8 +775,10 @@
 	add_local_bool(BOOL2_NAME);
 	CU_ASSERT(semanage_bool_query_local(sh, key, &resp) >= 0);
 	CU_ASSERT_PTR_NOT_NULL(resp);
+	semanage_bool_free(resp);
 
 	/* cleanup */
+	semanage_bool_key_free(key);
 	delete_local_bool(BOOL1_NAME);
 	delete_local_bool(BOOL2_NAME);
 	cleanup_handle(SH_TRANS);
@@ -784,6 +810,7 @@
 	CU_ASSERT(resp == 0);
 
 	/* cleanup */
+	semanage_bool_key_free(key);
 	cleanup_handle(SH_TRANS);
 }
 
@@ -918,12 +945,17 @@
 	CU_ASSERT(semanage_bool_list_local(sh, &records, &count) >= 0);
 	CU_ASSERT(count == init_count + 1);
 	CU_ASSERT_PTR_NOT_NULL(records[0]);
+	semanage_bool_free(records[0]);
+	free(records);
 
 	add_local_bool(BOOL2_NAME);
 	CU_ASSERT(semanage_bool_list_local(sh, &records, &count) >= 0);
 	CU_ASSERT(count == init_count + 2);
 	CU_ASSERT_PTR_NOT_NULL(records[0]);
 	CU_ASSERT_PTR_NOT_NULL(records[1]);
+	semanage_bool_free(records[0]);
+	semanage_bool_free(records[1]);
+	free(records);
 
 	/* cleanup */
 	delete_local_bool(BOOL1_NAME);
diff --git a/libsemanage/tests/test_fcontext.c b/libsemanage/tests/test_fcontext.c
index 62af711..a5fcf84 100644
--- a/libsemanage/tests/test_fcontext.c
+++ b/libsemanage/tests/test_fcontext.c
@@ -214,6 +214,8 @@
 		if (i != (unsigned int) idx)
 			semanage_fcontext_free(records[i]);
 
+	free(records);
+
 	return fcontext;
 }
 
@@ -230,6 +232,8 @@
 	CU_ASSERT_FATAL(semanage_fcontext_key_extract(sh, fcontext, &key) >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
+	semanage_fcontext_free(fcontext);
+
 	return key;
 }
 
@@ -246,6 +250,10 @@
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
 	CU_ASSERT_FATAL(semanage_fcontext_modify_local(sh, key, fcontext) >= 0);
+
+	/* cleanup */
+	semanage_fcontext_key_free(key);
+	semanage_fcontext_free(fcontext);
 }
 
 void delete_local_fcontext(int fcontext_idx)
@@ -257,6 +265,8 @@
 	key = get_fcontext_key_nth(fcontext_idx);
 
 	CU_ASSERT_FATAL(semanage_fcontext_del_local(sh, key) >= 0);
+
+	semanage_fcontext_key_free(key);
 }
 
 semanage_fcontext_key_t *get_fcontext_key_from_str(const char *str, int type)
@@ -477,6 +487,7 @@
 	}
 
 	/* cleanup */
+	semanage_context_free(con);
 	semanage_fcontext_free(fcontext);
 	cleanup_handle(level);
 }
@@ -587,12 +598,14 @@
 		CU_ASSERT(res >= 0);
 		const char *expr = semanage_fcontext_get_expr(resp);
 		CU_ASSERT_STRING_EQUAL(expr, fcontext_expr);
+		semanage_fcontext_free(resp);
 	} else {
 		CU_ASSERT(res < 0);
 		CU_ASSERT(resp == (void *) 42);
 	}
 
 	/* cleanup */
+	semanage_fcontext_key_free(key);
 	cleanup_handle(level);
 }
 
@@ -752,6 +765,8 @@
 	for (unsigned int i = 0; i < count; i++)
 		semanage_fcontext_free(records[i]);
 
+	free(records);
+
 	/* cleanup */
 	cleanup_handle(level);
 }
@@ -768,7 +783,7 @@
 				      const char *con_str, int exp_res)
 {
 	semanage_fcontext_t *fcontext;
-	semanage_fcontext_t *fcontext_local;
+	semanage_fcontext_t *fcontext_local = NULL;
 	semanage_fcontext_key_t *key = NULL;
 	semanage_context_t *con = NULL;
 	int res;
@@ -803,6 +818,8 @@
 					                &fcontext_local) >= 0);
 		CU_ASSERT(semanage_fcontext_compare2(fcontext_local,
 						     fcontext) == 0);
+		semanage_fcontext_free(fcontext_local);
+
 		CU_ASSERT(semanage_fcontext_del_local(sh, key) >= 0);
 		CU_ASSERT(semanage_fcontext_query_local(sh, key,
 					                &fcontext_local) < 0);
@@ -811,6 +828,7 @@
 	}
 
 	/* cleanup */
+	semanage_context_free(con);
 	semanage_fcontext_key_free(key);
 	semanage_fcontext_free(fcontext);
 	cleanup_handle(level);
@@ -846,6 +864,7 @@
 	/* transaction */
 	setup_handle(SH_TRANS);
 
+	semanage_fcontext_key_free(key);
 	key = get_fcontext_key_nth(I_FIRST);
 	CU_ASSERT(semanage_fcontext_query_local(sh, key, &resp) < 0);
 	CU_ASSERT_PTR_NULL(resp);
@@ -853,14 +872,19 @@
 	add_local_fcontext(I_FIRST);
 	CU_ASSERT(semanage_fcontext_query_local(sh, key, &resp) >= 0);
 	CU_ASSERT_PTR_NOT_NULL(resp);
+	semanage_fcontext_free(resp);
+	resp = NULL;
 
 	semanage_fcontext_key_free(key);
 	key = get_fcontext_key_nth(I_SECOND);
 	add_local_fcontext(I_SECOND);
 	CU_ASSERT(semanage_fcontext_query_local(sh, key, &resp) >= 0);
 	CU_ASSERT_PTR_NOT_NULL(resp);
+	semanage_fcontext_free(resp);
+	resp = NULL;
 
 	/* cleanup */
+	semanage_fcontext_key_free(key);
 	delete_local_fcontext(I_FIRST);
 	delete_local_fcontext(I_SECOND);
 	cleanup_handle(SH_TRANS);
@@ -898,6 +922,7 @@
 	CU_ASSERT(resp == 0);
 
 	/* cleanup */
+	semanage_fcontext_key_free(key);
 	cleanup_handle(SH_TRANS);
 }
 
@@ -1031,12 +1056,17 @@
 	CU_ASSERT(semanage_fcontext_list_local(sh, &records, &count) >= 0);
 	CU_ASSERT(count == 1);
 	CU_ASSERT_PTR_NOT_NULL(records[0]);
+	semanage_fcontext_free(records[0]);
+	free(records);
 
 	add_local_fcontext(I_SECOND);
 	CU_ASSERT(semanage_fcontext_list_local(sh, &records, &count) >= 0);
 	CU_ASSERT(count == 2);
 	CU_ASSERT_PTR_NOT_NULL(records[0]);
 	CU_ASSERT_PTR_NOT_NULL(records[1]);
+	semanage_fcontext_free(records[0]);
+	semanage_fcontext_free(records[1]);
+	free(records);
 
 	/* cleanup */
 	delete_local_fcontext(I_FIRST);
diff --git a/libsemanage/tests/test_ibendport.c b/libsemanage/tests/test_ibendport.c
index 79a8e2c..8addc90 100644
--- a/libsemanage/tests/test_ibendport.c
+++ b/libsemanage/tests/test_ibendport.c
@@ -113,6 +113,8 @@
 		if (i != (unsigned int) idx)
 			semanage_ibendport_free(records[i]);
 
+	free(records);
+
 	return ibendport;
 }
 
@@ -132,6 +134,8 @@
 	CU_ASSERT_FATAL(res >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
+	semanage_ibendport_free(ibendport);
+
 	return key;
 }
 
@@ -148,6 +152,9 @@
 
 	CU_ASSERT_FATAL(semanage_ibendport_modify_local(sh, key,
 							ibendport) >= 0);
+
+	semanage_ibendport_key_free(key);
+	semanage_ibendport_free(ibendport);
 }
 
 void delete_local_ibendport(int idx)
@@ -155,6 +162,8 @@
 	semanage_ibendport_key_t *key = NULL;
 	key = get_ibendport_key_nth(idx);
 	CU_ASSERT_FATAL(semanage_ibendport_del_local(sh, key) >= 0);
+
+	semanage_ibendport_key_free(key);
 }
 
 /* Function semanage_ibendport_query */
@@ -195,7 +204,9 @@
 	CU_ASSERT_CONTEXT_EQUAL(con, con_exp);
 
 	/* cleanup */
+	free(name_exp);
 	free(name);
+	semanage_ibendport_key_free(key);
 	semanage_ibendport_free(ibendport);
 	semanage_ibendport_free(ibendport_exp);
 	cleanup_handle(SH_CONNECT);
@@ -356,12 +367,14 @@
 	CU_ASSERT(semanage_ibendport_query_local(sh, key,
 						 &ibendport_local) >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(ibendport_local);
+	semanage_ibendport_free(ibendport_local);
 
 	CU_ASSERT(semanage_ibendport_del_local(sh, key) >= 0);
 	CU_ASSERT(semanage_ibendport_query_local(sh, key,
 						 &ibendport_local) < 0);
 
 	/* cleanup */
+	semanage_ibendport_key_free(key);
 	semanage_ibendport_free(ibendport);
 	cleanup_handle(SH_TRANS);
 }
diff --git a/libsemanage/tests/test_iface.c b/libsemanage/tests/test_iface.c
index d5d530a..434372f 100644
--- a/libsemanage/tests/test_iface.c
+++ b/libsemanage/tests/test_iface.c
@@ -139,6 +139,8 @@
 		if (i != (unsigned int) idx)
 			semanage_iface_free(records[i]);
 
+	free(records);
+
 	return iface;
 }
 
@@ -157,6 +159,9 @@
 	CU_ASSERT_FATAL(res >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
+	/* cleanup */
+	semanage_iface_free(iface);
+
 	return key;
 }
 
@@ -171,6 +176,10 @@
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
 	CU_ASSERT_FATAL(semanage_iface_modify_local(sh, key, iface) >= 0);
+
+	/* cleanup */
+	semanage_iface_key_free(key);
+	semanage_iface_free(iface);
 }
 
 void delete_local_iface(int idx)
@@ -178,6 +187,9 @@
 	semanage_iface_key_t *key = NULL;
 	key = get_iface_key_nth(idx);
 	CU_ASSERT_FATAL(semanage_iface_del_local(sh, key) >= 0);
+
+	/* cleanup */
+	semanage_iface_key_free(key);
 }
 
 /* Function semanage_iface_compare */
@@ -309,6 +321,7 @@
 	CU_ASSERT_CONTEXT_EQUAL(con1, con2);
 
 	/* cleanup */
+	semanage_context_free(con1);
 	semanage_iface_free(iface);
 	cleanup_handle(SH_CONNECT);
 }
@@ -332,6 +345,7 @@
 	CU_ASSERT_CONTEXT_EQUAL(con1, con2);
 
 	/* cleanup */
+	semanage_context_free(con1);
 	semanage_iface_free(iface);
 	cleanup_handle(SH_CONNECT);
 }
@@ -357,6 +371,8 @@
 	CU_ASSERT(semanage_iface_set_msgcon(sh, iface, msgcon) >= 0);
 
 	/* cleanup */
+	semanage_context_free(msgcon);
+	semanage_context_free(ifcon);
 	semanage_iface_free(iface);
 	cleanup_handle(SH_CONNECT);
 }
@@ -393,6 +409,8 @@
 	CU_ASSERT_CONTEXT_EQUAL(msgcon, msgcon2);
 
 	/* cleanup */
+	semanage_context_free(msgcon);
+	semanage_context_free(ifcon);
 	semanage_iface_free(iface);
 	semanage_iface_free(iface_clone);
 	cleanup_handle(SH_CONNECT);
@@ -426,6 +444,7 @@
 	CU_ASSERT_CONTEXT_EQUAL(con, con_exp);
 
 	/* cleanup */
+	semanage_iface_key_free(key);
 	semanage_iface_free(iface);
 	semanage_iface_free(iface_exp);
 	cleanup_handle(SH_CONNECT);
@@ -513,6 +532,8 @@
 	for (unsigned int i = 0; i < count; i++)
 		semanage_iface_free(records[i]);
 
+	free(records);
+
 	/* cleanup */
 	cleanup_handle(SH_CONNECT);
 }
@@ -541,11 +562,13 @@
 
 	CU_ASSERT(semanage_iface_query_local(sh, key, &iface_local) >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(iface_local);
+	semanage_iface_free(iface_local);
 
 	CU_ASSERT(semanage_iface_del_local(sh, key) >= 0);
 	CU_ASSERT(semanage_iface_query_local(sh, key, &iface_local) < 0);
 
 	/* cleanup */
+	semanage_iface_key_free(key);
 	semanage_iface_free(iface);
 	cleanup_handle(SH_TRANS);
 }
@@ -658,6 +681,7 @@
 	/* cleanup */
 	for (unsigned int i = 0; i < count; i++)
 		semanage_iface_free(records[i]);
+	free(records);
 
 	delete_local_iface(I_FIRST);
 	delete_local_iface(I_SECOND);
diff --git a/libsemanage/tests/test_node.c b/libsemanage/tests/test_node.c
index 53c2eb6..e49e8c3 100644
--- a/libsemanage/tests/test_node.c
+++ b/libsemanage/tests/test_node.c
@@ -148,6 +148,8 @@
 		if (i != (unsigned int) idx)
 			semanage_node_free(records[i]);
 
+	free(records);
+
 	return node;
 }
 
@@ -167,6 +169,8 @@
 	CU_ASSERT_FATAL(res >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
+	semanage_node_free(node);
+
 	return key;
 }
 
@@ -181,6 +185,10 @@
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
 	CU_ASSERT_FATAL(semanage_node_modify_local(sh, key, node) >= 0);
+
+	/* cleanup */
+	semanage_node_key_free(key);
+	semanage_node_free(node);
 }
 
 void delete_local_node(int idx)
@@ -190,6 +198,9 @@
 	key = get_node_key_nth(idx);
 
 	CU_ASSERT_FATAL(semanage_node_del_local(sh, key) >= 0);
+
+	/* cleanup */
+	semanage_node_key_free(key);
 }
 
 /* Function semanage_node_compare */
@@ -305,6 +316,7 @@
 	CU_ASSERT_STRING_EQUAL(addr, "192.168.0.1");
 
 	/* cleanup */
+	free(addr);
 	semanage_node_free(node);
 	cleanup_handle(SH_CONNECT);
 }
@@ -334,6 +346,7 @@
 		CU_ASSERT(addr1[i] == addr2[i]);
 
 	/* cleanup */
+	free(addr2);
 	semanage_node_free(node);
 	cleanup_handle(SH_CONNECT);
 }
@@ -357,6 +370,7 @@
 	CU_ASSERT_STRING_EQUAL(mask, "255.255.255.0");
 
 	/* cleanup */
+	free(mask);
 	semanage_node_free(node);
 	cleanup_handle(SH_CONNECT);
 }
@@ -386,6 +400,7 @@
 		CU_ASSERT(mask1[i] == mask2[i]);
 
 	/* cleanup */
+	free(mask2);
 	semanage_node_free(node);
 	cleanup_handle(SH_CONNECT);
 }
@@ -436,6 +451,7 @@
 	CU_ASSERT_CONTEXT_EQUAL(con1, con2);
 
 	/* cleanup */
+	semanage_context_free(con1);
 	semanage_node_free(node);
 	cleanup_handle(SH_CONNECT);
 }
@@ -461,6 +477,7 @@
 	CU_ASSERT(semanage_node_set_con(sh, node, con) >= 0);
 
 	/* cleanup */
+	semanage_context_free(con);
 	semanage_node_free(node);
 	cleanup_handle(SH_CONNECT);
 }
@@ -508,6 +525,9 @@
 	CU_ASSERT_CONTEXT_EQUAL(con, con2);
 
 	/* cleanup */
+	free(mask2);
+	free(addr2);
+	semanage_context_free(con);
 	semanage_node_free(node);
 	semanage_node_free(node_clone);
 	cleanup_handle(SH_CONNECT);
@@ -552,6 +572,8 @@
 	CU_ASSERT_CONTEXT_EQUAL(con, con_exp);
 
 	/* cleanup */
+	semanage_node_key_free(key);
+	semanage_node_free(node_exp);
 	semanage_node_free(node);
 	cleanup_handle(SH_CONNECT);
 }
@@ -638,6 +660,8 @@
 	for (unsigned int i = 0; i < count; i++)
 		semanage_node_free(records[i]);
 
+	free(records);
+
 	/* cleanup */
 	cleanup_handle(SH_CONNECT);
 }
@@ -679,6 +703,7 @@
 
 	CU_ASSERT(semanage_node_query_local(sh, key, &node_local) >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(node_local);
+	semanage_node_free(node_local);
 
 	CU_ASSERT(semanage_node_del_local(sh, key) >= 0);
 	CU_ASSERT(semanage_node_del_local(sh, key_tmp) >= 0);
@@ -686,6 +711,8 @@
 	CU_ASSERT(semanage_node_query_local(sh, key, &node_local) < 0);
 
 	/* cleanup */
+	semanage_node_key_free(key_tmp);
+	semanage_node_key_free(key);
 	semanage_node_free(node);
 	semanage_node_free(node_tmp);
 	cleanup_handle(SH_TRANS);
@@ -800,6 +827,8 @@
 	for (unsigned int i = 0; i < count; i++)
 		semanage_node_free(records[i]);
 
+	free(records);
+
 	delete_local_node(I_FIRST);
 	delete_local_node(I_SECOND);
 	delete_local_node(I_THIRD);
diff --git a/libsemanage/tests/test_other.c b/libsemanage/tests/test_other.c
index c4ee0ed..0a57e24 100644
--- a/libsemanage/tests/test_other.c
+++ b/libsemanage/tests/test_other.c
@@ -81,6 +81,9 @@
 	assert(str);
 	CU_ASSERT_STRING_EQUAL(str, "user_u:role_r:type_t:s0");
 
+	semanage_context_free(con);
+	con = NULL;
+
 	CU_ASSERT(semanage_context_from_string(sh, "my_u:my_r:my_t:s0",
 					       &con) >= 0);
 	CU_ASSERT_STRING_EQUAL(semanage_context_get_user(con), "my_u");
@@ -95,6 +98,7 @@
 	CU_ASSERT_STRING_EQUAL(semanage_context_get_mls(con_clone), "s0");
 
 	/* cleanup */
+	free(str);
 	semanage_context_free(con);
 	semanage_context_free(con_clone);
 	cleanup_handle(SH_CONNECT);
@@ -115,6 +119,8 @@
 	CU_ASSERT(semanage_module_info_set_priority(sh, modinfo, -42) < 0);
 
 	/* cleanup */
+	semanage_module_info_destroy(sh, modinfo);
+	free(modinfo);
 	CU_ASSERT(semanage_disconnect(sh) >= 0);
 	semanage_handle_destroy(sh);
 }
diff --git a/libsemanage/tests/test_port.c b/libsemanage/tests/test_port.c
index 0408be4..f4c6ec2 100644
--- a/libsemanage/tests/test_port.c
+++ b/libsemanage/tests/test_port.c
@@ -146,6 +146,8 @@
 		if (i != (unsigned int) idx)
 			semanage_port_free(records[i]);
 
+	free(records);
+
 	return port;
 }
 
@@ -165,6 +167,9 @@
 	CU_ASSERT_FATAL(res >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
+	/* cleanup */
+	semanage_port_free(port);
+
 	return key;
 }
 
@@ -181,6 +186,10 @@
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
 	CU_ASSERT_FATAL(semanage_port_modify_local(sh, key, port) >= 0);
+
+	/* cleanup */
+	semanage_port_key_free(key);
+	semanage_port_free(port);
 }
 
 void delete_local_port(int port_idx)
@@ -192,6 +201,8 @@
 	key = get_port_key_nth(port_idx);
 
 	CU_ASSERT_FATAL(semanage_port_del_local(sh, key) >= 0);
+
+	semanage_port_key_free(key);
 }
 
 /* Function semanage_port_compare */
@@ -447,6 +458,7 @@
 	CU_ASSERT_CONTEXT_EQUAL(con, con2);
 
 	/* cleanup */
+	semanage_context_free(con);
 	semanage_port_free(port);
 	semanage_port_free(port_clone);
 	cleanup_handle(SH_CONNECT);
@@ -480,6 +492,7 @@
 	CU_ASSERT_CONTEXT_EQUAL(con, con_exp);
 
 	/* cleanup */
+	semanage_port_key_free(key);
 	semanage_port_free(port);
 	semanage_port_free(port_exp);
 	cleanup_handle(SH_CONNECT);
@@ -567,6 +580,8 @@
 	for (unsigned int i = 0; i < count; i++)
 		semanage_port_free(records[i]);
 
+	free(records);
+
 	cleanup_handle(SH_CONNECT);
 }
 
@@ -594,11 +609,14 @@
 
 	con_local = semanage_port_get_con(port_local);
 	CU_ASSERT_CONTEXT_EQUAL(con, con_local);
+	semanage_port_free(port_local);
 
 	CU_ASSERT(semanage_port_del_local(sh, key) >= 0);
 	CU_ASSERT(semanage_port_query_local(sh, key, &port_local) < 0);
 
 	/* cleanup */
+	semanage_context_free(con);
+	semanage_port_key_free(key);
 	semanage_port_free(port);
 	cleanup_handle(SH_TRANS);
 }
@@ -633,6 +651,7 @@
 
 	/* cleanup */
 	delete_local_port(I_FIRST);
+	semanage_port_key_free(key);
 	semanage_port_free(port);
 	semanage_port_free(port_exp);
 	cleanup_handle(SH_TRANS);
@@ -747,6 +766,8 @@
 	for (unsigned int i = 0; i < count; i++)
 		semanage_port_free(records[i]);
 
+	free(records);
+
 	delete_local_port(I_FIRST);
 	delete_local_port(I_SECOND);
 	delete_local_port(I_THIRD);
@@ -773,6 +794,7 @@
 	helper_commit();
 
 	/* cleanup */
+	semanage_port_key_free(key);
 	helper_begin_transaction();
 	delete_local_port(I_FIRST);
 	cleanup_handle(SH_TRANS);
@@ -832,6 +854,8 @@
 	helper_begin_transaction();
 	CU_ASSERT(semanage_port_del_local(sh, key1) >= 0);
 	CU_ASSERT(semanage_port_del_local(sh, key2) >= 0);
+	semanage_context_free(con2);
+	semanage_context_free(con1);
 	semanage_port_key_free(key1);
 	semanage_port_key_free(key2);
 	semanage_port_free(port1);
diff --git a/libsemanage/tests/test_user.c b/libsemanage/tests/test_user.c
index cd08203..c3835c8 100644
--- a/libsemanage/tests/test_user.c
+++ b/libsemanage/tests/test_user.c
@@ -130,6 +130,8 @@
 		if (i != (unsigned int) idx)
 			semanage_user_free(records[i]);
 
+	free(records);
+
 	return user;
 }
 
@@ -149,6 +151,8 @@
 	CU_ASSERT_FATAL(res >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
+	semanage_user_free(user);
+
 	return key;
 }
 
@@ -165,6 +169,9 @@
 	CU_ASSERT_PTR_NOT_NULL_FATAL(key);
 
 	CU_ASSERT_FATAL(semanage_user_modify_local(sh, key, user) >= 0);
+
+	semanage_user_key_free(key);
+	semanage_user_free(user);
 }
 
 void delete_local_user(int user_idx)
@@ -176,6 +183,8 @@
 	key = get_user_key_nth(user_idx);
 
 	CU_ASSERT_FATAL(semanage_user_del_local(sh, key) >= 0);
+
+	semanage_user_key_free(key);
 }
 
 /* Function semanage_user_compare */
@@ -391,6 +400,7 @@
 	CU_ASSERT(semanage_user_get_num_roles(user) == 0);
 
 	/* cleanup */
+	free(roles_arr);
 	semanage_user_free(user);
 	cleanup_handle(SH_CONNECT);
 }
@@ -459,6 +469,7 @@
 	CU_ASSERT_PTR_NOT_NULL(user);
 
 	/* cleanup */
+	semanage_user_key_free(key);
 	semanage_user_free(user);
 	cleanup_handle(SH_CONNECT);
 }
@@ -546,6 +557,8 @@
 	for (unsigned int i = 0; i < count; i++)
 		semanage_user_free(records[i]);
 
+	free(records);
+
 	cleanup_handle(SH_CONNECT);
 }
 
@@ -573,10 +586,12 @@
 
 	CU_ASSERT(semanage_user_query_local(sh, key, &user_local) >= 0);
 	CU_ASSERT_PTR_NOT_NULL_FATAL(user_local);
+	semanage_user_free(user_local);
 	CU_ASSERT(semanage_user_del_local(sh, key) >= 0);
 	CU_ASSERT(semanage_user_query_local(sh, key, &user_local) < 0);
 
 	/* cleanup */
+	semanage_user_key_free(key);
 	semanage_user_free(user);
 	cleanup_handle(SH_TRANS);
 }
@@ -683,6 +698,8 @@
 	for (unsigned int i = 0; i < count; i++)
 		semanage_user_free(records[i]);
 
+	free(records);
+
 	delete_local_user(I_FIRST);
 	delete_local_user(I_SECOND);
 	delete_local_user(I_THIRD);
diff --git a/libsemanage/tests/utilities.c b/libsemanage/tests/utilities.c
index 1839321..b28ae15 100644
--- a/libsemanage/tests/utilities.c
+++ b/libsemanage/tests/utilities.c
@@ -99,6 +99,7 @@
 	char *buf = NULL;
 	size_t len = 0;
 	FILE *fptr = fopen(filename, "rb");
+	int rc;
 
 	if (!fptr) {
 		perror("fopen");
@@ -120,7 +121,9 @@
 	fread(buf, len, 1, fptr);
 	fclose(fptr);
 
-	return write_test_policy(buf, len);
+	rc = write_test_policy(buf, len);
+	free(buf);
+	return rc;
 }
 
 int write_test_policy_src(unsigned char *data, unsigned int data_len) {
diff --git a/libsemanage/tests/utilities.h b/libsemanage/tests/utilities.h
index db4dabf..298b328 100644
--- a/libsemanage/tests/utilities.h
+++ b/libsemanage/tests/utilities.h
@@ -39,6 +39,8 @@
 		CU_ASSERT(semanage_context_to_string(sh, CON1, &__str) >= 0); \
 		CU_ASSERT(semanage_context_to_string(sh, CON2, &__str2) >= 0); \
 		CU_ASSERT_STRING_EQUAL(__str, __str2); \
+		free(__str2); \
+		free(__str); \
 	} while (0)
 
 
diff --git a/libsepol/Android.bp b/libsepol/Android.bp
index 5232a68..6135745 100644
--- a/libsepol/Android.bp
+++ b/libsepol/Android.bp
@@ -33,21 +33,32 @@
     ],
 }
 
-common_CFLAGS = [
-    "-D_GNU_SOURCE",
-    "-Wall",
-    "-Werror",
-    "-W",
-    "-Wundef",
-    "-Wshadow",
-    "-Wno-error=missing-noreturn",
-    "-Wmissing-format-attribute",
-]
+cc_defaults {
+    name: "libsepol_defaults",
+    cflags: [
+      "-D_GNU_SOURCE",
+      "-Wall",
+      "-Werror",
+      "-W",
+      "-Wundef",
+      "-Wshadow",
+      "-Wno-error=missing-noreturn",
+      "-Wmissing-format-attribute",
+    ],
+    target: {
+        bionic: {
+            cflags: ["-DHAVE_REALLOCARRAY"]
+        },
+        musl: {
+            cflags: ["-DHAVE_REALLOCARRAY"]
+        }
+    }
+}
 
 cc_library {
     name: "libsepol",
+    defaults: ["libsepol_defaults"],
     host_supported: true,
-    cflags: common_CFLAGS,
     srcs: [
         "src/assertion.c",
         "src/avrule_block.c",
@@ -135,7 +146,7 @@
 
 cc_binary_host {
     name: "chkcon",
+    defaults: ["libsepol_defaults"],
     srcs: ["utils/chkcon.c"],
     shared_libs: ["libsepol"],
-    cflags: common_CFLAGS,
 }
diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c
index 4cc7f87..38edcf8 100644
--- a/libsepol/cil/src/cil.c
+++ b/libsepol/cil/src/cil.c
@@ -1456,6 +1456,12 @@
 
 		buf_pos = snprintf(str_tmp, str_len, "user %s prefix %s;\n", user->datum.fqn,
 									userprefix->prefix_str);
+		if (buf_pos < 0) {
+			free(str_tmp);
+			*size = 0;
+			*out = NULL;
+			goto exit;
+		}
 		str_len -= buf_pos;
 		str_tmp += buf_pos;
 	}
@@ -1765,6 +1771,9 @@
 		str_tmp += buf_pos;
 
 		switch(filecon->type) {
+		case CIL_FILECON_ANY:
+			str_type = "";
+			break;
 		case CIL_FILECON_FILE:
 			str_type = "\t--";
 			break;
@@ -2530,7 +2539,7 @@
 	*filecon = cil_malloc(sizeof(**filecon));
 
 	(*filecon)->path_str = NULL;
-	(*filecon)->type = 0;
+	(*filecon)->type = CIL_FILECON_ANY;
 	(*filecon)->context_str = NULL;
 	(*filecon)->context = NULL;
 }
@@ -2574,6 +2583,7 @@
 
 	(*genfscon)->fs_str = NULL;
 	(*genfscon)->path_str = NULL;
+	(*genfscon)->file_type = CIL_FILECON_ANY;
 	(*genfscon)->context_str = NULL;
 	(*genfscon)->context = NULL;
 }
diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
index d8aa495..53017e2 100644
--- a/libsepol/cil/src/cil_binary.c
+++ b/libsepol/cil/src/cil_binary.c
@@ -2823,6 +2823,12 @@
 		goto exit;
 	}
 
+	if (sepol_constrain->permissions == 0) {
+		/* No permissions, so don't insert rule. */
+		free(sepol_constrain);
+		return SEPOL_OK;
+	}
+
 	rc = __cil_constrain_expr_to_sepol_expr(pdb, db, expr, &sepol_expr);
 	if (rc != SEPOL_OK) {
 		goto exit;
@@ -3462,6 +3468,43 @@
 
 		new_ocon->u.name = cil_strdup(cil_genfscon->path_str);
 
+		if (cil_genfscon->file_type != CIL_FILECON_ANY) {
+			class_datum_t *class_datum;
+			const char *class_name;
+			switch (cil_genfscon->file_type) {
+			case CIL_FILECON_FILE:
+				class_name = "file";
+				break;
+			case CIL_FILECON_DIR:
+				class_name = "dir";
+				break;
+			case CIL_FILECON_CHAR:
+				class_name = "chr_file";
+				break;
+			case CIL_FILECON_BLOCK:
+				class_name = "blk_file";
+				break;
+			case CIL_FILECON_SOCKET:
+				class_name = "sock_file";
+				break;
+			case CIL_FILECON_PIPE:
+				class_name = "fifo_file";
+				break;
+			case CIL_FILECON_SYMLINK:
+				class_name = "lnk_file";
+				break;
+			default:
+				rc = SEPOL_ERR;
+				goto exit;
+			}
+			class_datum = hashtab_search(pdb->p_classes.table, class_name);
+			if (!class_datum) {
+				rc = SEPOL_ERR;
+				goto exit;
+			}
+			new_ocon->v.sclass = class_datum->s.value;
+		}
+
 		rc = __cil_context_to_sepol_context(pdb, cil_genfscon->context, &new_ocon->context[0]);
 		if (rc != SEPOL_OK) {
 			goto exit;
@@ -4603,6 +4646,9 @@
 	char *neverallow_str;
 	char *allow_str;
 	enum cil_flavor avrule_flavor;
+	int num_matching = 0;
+	int count_matching = 0;
+	enum cil_log_level log_level = cil_get_log_level();
 
 	target.rule_kind = CIL_AVRULE_ALLOWED;
 	target.is_extended = cil_rule->is_extended;
@@ -4630,10 +4676,18 @@
 	}
 
 	cil_list_for_each(i2, matching) {
+		num_matching++;
+	}
+	cil_list_for_each(i2, matching) {
 		n2 = i2->data;
 		r2 = n2->data;
 		__cil_print_parents("    ", n2);
 		__cil_print_rule("      ", allow_str, r2);
+		count_matching++;
+		if (count_matching >= 4 && num_matching > 4 && log_level == CIL_ERR) {
+			cil_log(CIL_ERR, "    Only first 4 of %d matching rules shown (use \"-v\" to show all)\n", num_matching);
+			break;
+		}
 	}
 	cil_log(CIL_ERR,"\n");
 	cil_list_destroy(&matching, CIL_FALSE);
@@ -4826,6 +4880,7 @@
 			struct cil_avrule target;
 			struct cil_tree_node *n1 = NULL;
 			int count_bad = 0;
+			enum cil_log_level log_level = cil_get_log_level();
 
 			*violation = CIL_TRUE;
 
@@ -4872,16 +4927,16 @@
 						__cil_print_rule("      ", "allow", r2);
 					}
 					count_matching++;
-					if (count_matching >= 2) {
-						cil_log(CIL_ERR, "    Only first 2 of %d matching rules shown\n", num_matching);
+					if (count_matching >= 2 && num_matching > 2 && log_level == CIL_ERR) {
+						cil_log(CIL_ERR, "    Only first 2 of %d matching rules shown (use \"-v\" to show all)\n", num_matching);
 						break;
 					}
 				}
 				cil_list_destroy(&matching, CIL_FALSE);
 				cil_list_destroy(&target.perms.classperms, CIL_TRUE);
 				count_bad++;
-				if (count_bad >= 2) {
-					cil_log(CIL_ERR, "  Only first 2 of %d bad rules shown\n", numbad);
+				if (count_bad >= 4 && numbad > 4 && log_level == CIL_ERR) {
+					cil_log(CIL_ERR, "  Only first 4 of %d bad rules shown (use \"-v\" to show all)\n", numbad);
 					break;
 				}
 			}
diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c
index 9c34be2..5f9392d 100644
--- a/libsepol/cil/src/cil_build_ast.c
+++ b/libsepol/cil/src/cil_build_ast.c
@@ -4229,7 +4229,9 @@
 
 	filecon->path_str = parse_current->next->data;
 
-	if (type == CIL_KEY_FILE) {
+	if (type == CIL_KEY_ANY) {
+		filecon->type = CIL_FILECON_ANY;
+	} else if (type == CIL_KEY_FILE) {
 		filecon->type = CIL_FILECON_FILE;
 	} else if (type == CIL_KEY_DIR) {
 		filecon->type = CIL_FILECON_DIR;
@@ -4243,8 +4245,6 @@
 		filecon->type = CIL_FILECON_PIPE;
 	} else if (type == CIL_KEY_SYMLINK) {
 		filecon->type = CIL_FILECON_SYMLINK;
-	} else if (type == CIL_KEY_ANY) {
-		filecon->type = CIL_FILECON_ANY;
 	} else {
 		cil_log(CIL_ERR, "Invalid file type\n");
 		rc = SEPOL_ERR;
@@ -4572,9 +4572,11 @@
 		CIL_SYN_STRING,
 		CIL_SYN_STRING,
 		CIL_SYN_STRING | CIL_SYN_LIST,
+		CIL_SYN_STRING | CIL_SYN_LIST | CIL_SYN_END,
 		CIL_SYN_END
 	};
 	size_t syntax_len = sizeof(syntax)/sizeof(*syntax);
+	struct cil_tree_node *context_node;
 	int rc = SEPOL_ERR;
 	struct cil_genfscon *genfscon = NULL;
 
@@ -4592,15 +4594,48 @@
 	genfscon->fs_str = parse_current->next->data;
 	genfscon->path_str = parse_current->next->next->data;
 
-	if (parse_current->next->next->next->cl_head == NULL ) {
-		genfscon->context_str = parse_current->next->next->next->data;
+	if (parse_current->next->next->next->next) {
+		/* (genfscon <FS_STR> <PATH_STR> <FILE_TYPE> ... */
+		char *file_type = parse_current->next->next->next->data;
+		if (file_type == CIL_KEY_ANY) {
+			genfscon->file_type = CIL_FILECON_ANY;
+		} else if (file_type == CIL_KEY_FILE) {
+			genfscon->file_type = CIL_FILECON_FILE;
+		} else if (file_type == CIL_KEY_DIR) {
+			genfscon->file_type = CIL_FILECON_DIR;
+		} else if (file_type == CIL_KEY_CHAR) {
+			genfscon->file_type = CIL_FILECON_CHAR;
+		} else if (file_type == CIL_KEY_BLOCK) {
+			genfscon->file_type = CIL_FILECON_BLOCK;
+		} else if (file_type == CIL_KEY_SOCKET) {
+			genfscon->file_type = CIL_FILECON_SOCKET;
+		} else if (file_type == CIL_KEY_PIPE) {
+			genfscon->file_type = CIL_FILECON_PIPE;
+		} else if (file_type == CIL_KEY_SYMLINK) {
+			genfscon->file_type = CIL_FILECON_SYMLINK;
+		} else {
+			if (parse_current->next->next->next->cl_head) {
+				cil_log(CIL_ERR, "Expecting file type, but found a list\n");
+			} else {
+				cil_log(CIL_ERR, "Invalid file type \"%s\"\n", file_type);
+			}
+			rc = SEPOL_ERR;
+			goto exit;
+		}
+		context_node = parse_current->next->next->next->next;
 	} else {
-		cil_context_init(&genfscon->context);
+		/* (genfscon <FS_STR> <PATH_STR> ... */
+		context_node = parse_current->next->next->next;
+	}
 
-		rc = cil_fill_context(parse_current->next->next->next->cl_head, genfscon->context);
+	if (context_node->cl_head) {
+		cil_context_init(&genfscon->context);
+		rc = cil_fill_context(context_node->cl_head, genfscon->context);
 		if (rc != SEPOL_OK) {
 			goto exit;
 		}
+	} else {
+		genfscon->context_str = context_node->data;
 	}
 
 	ast_node->data = genfscon;
@@ -5668,10 +5703,10 @@
 		goto exit;
 	}
 
-	if (strchr(addr_node->data, '.') != NULL) {
-		addr->family = AF_INET;
-	} else {
+	if (strchr(addr_node->data, ':') != NULL) {
 		addr->family = AF_INET6;
+	} else {
+		addr->family = AF_INET;
 	}
 
 	rc = inet_pton(addr->family, addr_node->data, &addr->ip);
@@ -5683,7 +5718,7 @@
 	return SEPOL_OK;
 
 exit:
-	cil_log(CIL_ERR, "Bad ip address or netmask\n"); 
+	cil_log(CIL_ERR, "Bad ip address or netmask: %s\n", (addr_node && addr_node->data) ? (const char *)addr_node->data : "n/a");
 	return rc;
 }
 
diff --git a/libsepol/cil/src/cil_copy_ast.c b/libsepol/cil/src/cil_copy_ast.c
index 2fad972..a4ead9d 100644
--- a/libsepol/cil/src/cil_copy_ast.c
+++ b/libsepol/cil/src/cil_copy_ast.c
@@ -1725,6 +1725,12 @@
 		copy_func = &cil_copy_block;
 		break;
 	case CIL_BLOCKABSTRACT:
+		if (args->orig_dest->flavor == CIL_BLOCKINHERIT) {
+			/* When inheriting a block, don't copy any blockabstract
+			 * statements. Inheriting a block from a block that was
+			 * just inherited never worked. */
+			return SEPOL_OK;
+		}
 		copy_func = &cil_copy_blockabstract;
 		break;
 	case CIL_BLOCKINHERIT:
diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h
index 6f1d3cb..a760476 100644
--- a/libsepol/cil/src/cil_internal.h
+++ b/libsepol/cil/src/cil_internal.h
@@ -730,14 +730,14 @@
 };
 
 enum cil_filecon_types {
-	CIL_FILECON_FILE = 1,
+	CIL_FILECON_ANY = 0,
+	CIL_FILECON_FILE,
 	CIL_FILECON_DIR,
 	CIL_FILECON_CHAR,
 	CIL_FILECON_BLOCK,
 	CIL_FILECON_SOCKET,
 	CIL_FILECON_PIPE,
 	CIL_FILECON_SYMLINK,
-	CIL_FILECON_ANY
 };
 
 struct cil_filecon {
@@ -791,6 +791,7 @@
 struct cil_genfscon {
 	char *fs_str;
 	char *path_str;
+	enum cil_filecon_types file_type;
 	char *context_str;
 	struct cil_context *context;
 };
diff --git a/libsepol/cil/src/cil_log.c b/libsepol/cil/src/cil_log.c
index a8e4d2e..a296929 100644
--- a/libsepol/cil/src/cil_log.c
+++ b/libsepol/cil/src/cil_log.c
@@ -70,3 +70,8 @@
 {
 	cil_log_level = lvl;
 }
+
+enum cil_log_level cil_get_log_level(void)
+{
+	return cil_log_level;
+}
diff --git a/libsepol/cil/src/cil_log.h b/libsepol/cil/src/cil_log.h
index 541569b..442781f 100644
--- a/libsepol/cil/src/cil_log.h
+++ b/libsepol/cil/src/cil_log.h
@@ -38,4 +38,6 @@
 __attribute__ ((format(printf, 2, 0))) void cil_vlog(enum cil_log_level lvl, const char *msg, va_list args);
 __attribute__ ((format(printf, 2, 3))) void cil_log(enum cil_log_level lvl, const char *msg, ...);
 
+enum cil_log_level cil_get_log_level(void);
+
 #endif // CIL_LOG_H_
diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c
index 7e2c2b9..09c02af 100644
--- a/libsepol/cil/src/cil_post.c
+++ b/libsepol/cil/src/cil_post.c
@@ -2280,8 +2280,10 @@
 static int __cil_post_process_context_rules(struct cil_sort *sort, int (*compar)(const void *, const void *), int (*concompar)(const void *, const void *), struct cil_db *db, enum cil_flavor flavor, const char *flavor_str)
 {
 	uint32_t count = sort->count;
-	uint32_t i, j = 0, removed = 0;
+	uint32_t i = 0, j, removed = 0;
+	int conflicting = 0;
 	int rc = SEPOL_OK;
+	enum cil_log_level log_level = cil_get_log_level();
 
 	if (count < 2) {
 		return SEPOL_OK;
@@ -2289,36 +2291,43 @@
 
 	qsort(sort->array, sort->count, sizeof(sort->array), compar);
 
-	for (i=1; i<count; i++) {
+	for (j=1; j<count; j++) {
 		if (compar(&sort->array[i], &sort->array[j]) != 0) {
-			j++;
+			i++;
+			if (conflicting >= 4) {
+				/* 2 rules were written when conflicting == 1 */
+				cil_log(CIL_WARN, "  Only first 4 of %d conflicting rules shown\n", conflicting);
+			}
+			conflicting = 0;
 		} else {
 			removed++;
-			if (!db->multiple_decls ||
-			   concompar(&sort->array[i], &sort->array[j]) != 0) {
-				struct cil_list_item li;
-				int rc2;
-				cil_log(CIL_WARN, "Found conflicting %s rules\n",
-					flavor_str);
-				rc = SEPOL_ERR;
-				li.flavor = flavor;
-				li.data = sort->array[i];
-				rc2 = cil_tree_walk(db->ast->root,
-						    __cil_post_report_conflict,
-						    NULL, NULL, &li);
-				if (rc2 != SEPOL_OK) goto exit;
-				li.data = sort->array[j];
-				rc2 = cil_tree_walk(db->ast->root,
-						    __cil_post_report_conflict,
-						    NULL, NULL, &li);
-				if (rc2 != SEPOL_OK) goto exit;
+			if (!db->multiple_decls || concompar(&sort->array[i], &sort->array[j]) != 0) {
+				conflicting++;
+				if (log_level >= CIL_WARN) {
+					struct cil_list_item li;
+					int rc2;
+					li.flavor = flavor;
+					if (conflicting == 1) {
+						cil_log(CIL_WARN, "Found conflicting %s rules\n", flavor_str);
+						rc = SEPOL_ERR;
+						li.data = sort->array[i];
+						rc2 = cil_tree_walk(db->ast->root, __cil_post_report_conflict,
+											NULL, NULL, &li);
+						if (rc2 != SEPOL_OK) goto exit;
+					}
+					if (conflicting < 4 || log_level > CIL_WARN) {
+						li.data = sort->array[j];
+						rc2 = cil_tree_walk(db->ast->root, __cil_post_report_conflict,
+											NULL, NULL, &li);
+						if (rc2 != SEPOL_OK) goto exit;
+					}
+				}
 			}
 		}
-		if (i != j) {
-			sort->array[j] = sort->array[i];
+		if (i != j && !conflicting) {
+			sort->array[i] = sort->array[j];
 		}
 	}
-
 	sort->count = count - removed;
 
 exit:
diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c
index e97a9f4..69a8a2e 100644
--- a/libsepol/cil/src/cil_resolve_ast.c
+++ b/libsepol/cil/src/cil_resolve_ast.c
@@ -65,6 +65,7 @@
 	struct cil_list *sensitivityorder_lists;
 	struct cil_list *in_list_before;
 	struct cil_list *in_list_after;
+	struct cil_list *abstract_blocks;
 };
 
 static struct cil_name * __cil_insert_name(struct cil_db *db, hashtab_key_t key, struct cil_tree_node *ast_node)
@@ -754,6 +755,11 @@
 	if (rc != SEPOL_OK) {
 		goto exit;
 	}
+	if (NODE(class_datum)->flavor != CIL_CLASS) {
+		cil_log(CIL_ERR, "Class %s is not a kernel class and cannot be associated with common %s\n", clscom->class_str, clscom->common_str);
+		rc = SEPOL_ERR;
+		goto exit;
+	}
 
 	rc = cil_resolve_name(current, clscom->common_str, CIL_SYM_COMMONS, extra_args, &common_datum);
 	if (rc != SEPOL_OK) {
@@ -2379,11 +2385,25 @@
 	return rc;
 }
 
+static void cil_mark_subtree_abstract(struct cil_tree_node *node)
+{
+	struct cil_block *block = node->data;
+
+	block->is_abstract = CIL_TRUE;
+
+	for (node = node->cl_head; node; node = node->next) {
+		if (node->flavor == CIL_BLOCK) {
+			cil_mark_subtree_abstract(node);
+		}
+	}
+}
+
 int cil_resolve_blockabstract(struct cil_tree_node *current, void *extra_args)
 {
 	struct cil_blockabstract *abstract = current->data;
 	struct cil_symtab_datum *block_datum = NULL;
 	struct cil_tree_node *block_node = NULL;
+	struct cil_args_resolve *args = extra_args;
 	int rc = SEPOL_ERR;
 
 	rc = cil_resolve_name(current, abstract->block_str, CIL_SYM_BLOCKS, extra_args, &block_datum);
@@ -2398,7 +2418,7 @@
 		goto exit;
 	}
 
-	((struct cil_block*)block_datum)->is_abstract = CIL_TRUE;
+	cil_list_append(args->abstract_blocks, CIL_NODE, block_node);
 
 	return SEPOL_OK;
 
@@ -4084,6 +4104,7 @@
 	extra_args.sensitivityorder_lists = NULL;
 	extra_args.in_list_before = NULL;
 	extra_args.in_list_after = NULL;
+	extra_args.abstract_blocks = NULL;
 
 	cil_list_init(&extra_args.to_destroy, CIL_NODE);
 	cil_list_init(&extra_args.sidorder_lists, CIL_LIST_ITEM);
@@ -4093,6 +4114,7 @@
 	cil_list_init(&extra_args.sensitivityorder_lists, CIL_LIST_ITEM);
 	cil_list_init(&extra_args.in_list_before, CIL_IN);
 	cil_list_init(&extra_args.in_list_after, CIL_IN);
+	cil_list_init(&extra_args.abstract_blocks, CIL_NODE);
 
 	for (pass = CIL_PASS_TIF; pass < CIL_PASS_NUM; pass++) {
 		extra_args.pass = pass;
@@ -4116,6 +4138,13 @@
 			cil_list_destroy(&extra_args.in_list_after, CIL_FALSE);
 		}
 
+		if (pass == CIL_PASS_BLKABS) {
+			struct cil_list_item *item;
+			cil_list_for_each(item, extra_args.abstract_blocks) {
+				cil_mark_subtree_abstract(item->data);
+			}
+		}
+
 		if (pass == CIL_PASS_BLKIN_LINK) {
 			rc = cil_check_for_bad_inheritance(current);
 			if (rc != SEPOL_OK) {
@@ -4234,6 +4263,7 @@
 	cil_list_destroy(&extra_args.to_destroy, CIL_FALSE);
 	cil_list_destroy(&extra_args.in_list_before, CIL_FALSE);
 	cil_list_destroy(&extra_args.in_list_after, CIL_FALSE);
+	cil_list_destroy(&extra_args.abstract_blocks, CIL_FALSE);
 
 	return rc;
 }
@@ -4255,9 +4285,13 @@
 		case CIL_ROOT:
 			goto exit;
 			break;
-		case CIL_BLOCK:
-			symtab = &((struct cil_block*)node->data)->symtab[sym_index];
-			rc = cil_symtab_get_datum(symtab, name, datum);
+		case CIL_BLOCK: {
+			struct cil_block *block = node->data;
+			if (!block->is_abstract) {
+				symtab = &block->symtab[sym_index];
+				rc = cil_symtab_get_datum(symtab, name, datum);
+			}
+		}
 			break;
 		case CIL_BLOCKINHERIT: {
 			struct cil_blockinherit *inherit = node->data;
diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c
index 38374f1..c0ee747 100644
--- a/libsepol/cil/src/cil_write_ast.c
+++ b/libsepol/cil/src/cil_write_ast.c
@@ -1232,24 +1232,34 @@
 		struct cil_filecon *filecon = node->data;
 		fprintf(out, "(filecon ");
 		fprintf(out, "\"%s\" ", filecon->path_str);
-		if (filecon->type == CIL_FILECON_FILE)
-			fprintf(out, "%s ", CIL_KEY_FILE);
-		else if (filecon->type == CIL_FILECON_DIR)
-			fprintf(out, "%s ", CIL_KEY_DIR);
-		else if (filecon->type == CIL_FILECON_CHAR)
-			fprintf(out, "%s ", CIL_KEY_CHAR);
-		else if (filecon->type == CIL_FILECON_BLOCK)
-			fprintf(out, "%s ", CIL_KEY_BLOCK);
-		else if (filecon->type == CIL_FILECON_SOCKET)
-			fprintf(out, "%s ", CIL_KEY_SOCKET);
-		else if (filecon->type == CIL_FILECON_PIPE)
-			fprintf(out, "%s ", CIL_KEY_PIPE);
-		else if (filecon->type == CIL_FILECON_SYMLINK)
-			fprintf(out, "%s ", CIL_KEY_SYMLINK);
-		else if (filecon->type == CIL_FILECON_ANY)
+		switch (filecon->type) {
+		case CIL_FILECON_ANY:
 			fprintf(out, "%s ", CIL_KEY_ANY);
-		else
+			break;
+		case CIL_FILECON_FILE:
+			fprintf(out, "%s ", CIL_KEY_FILE);
+			break;
+		case CIL_FILECON_DIR:
+			fprintf(out, "%s ", CIL_KEY_DIR);
+			break;
+		case CIL_FILECON_CHAR:
+			fprintf(out, "%s ", CIL_KEY_CHAR);
+			break;
+		case CIL_FILECON_BLOCK:
+			fprintf(out, "%s ", CIL_KEY_BLOCK);
+			break;
+		case CIL_FILECON_SOCKET:
+			fprintf(out, "%s ", CIL_KEY_SOCKET);
+			break;
+		case CIL_FILECON_PIPE:
+			fprintf(out, "%s ", CIL_KEY_PIPE);
+			break;
+		case CIL_FILECON_SYMLINK:
+			fprintf(out, "%s ", CIL_KEY_SYMLINK);
+			break;
+		default:
 			fprintf(out, "<?FILETYPE> ");
+		}
 		if (filecon->context)
 			write_context(out, filecon->context, CIL_TRUE);
 		else if (filecon->context_str)
@@ -1318,6 +1328,33 @@
 		struct cil_genfscon *genfscon = node->data;
 		fprintf(out, "(genfscon ");
 		fprintf(out, "%s \"%s\" ", genfscon->fs_str, genfscon->path_str);
+		if (genfscon->file_type != CIL_FILECON_ANY) {
+			switch (genfscon->file_type) {
+			case CIL_FILECON_FILE:
+				fprintf(out, "%s ", CIL_KEY_FILE);
+				break;
+			case CIL_FILECON_DIR:
+				fprintf(out, "%s ", CIL_KEY_DIR);
+				break;
+			case CIL_FILECON_CHAR:
+				fprintf(out, "%s ", CIL_KEY_CHAR);
+				break;
+			case CIL_FILECON_BLOCK:
+				fprintf(out, "%s ", CIL_KEY_BLOCK);
+				break;
+			case CIL_FILECON_SOCKET:
+				fprintf(out, "%s ", CIL_KEY_SOCKET);
+				break;
+			case CIL_FILECON_PIPE:
+				fprintf(out, "%s ", CIL_KEY_PIPE);
+				break;
+			case CIL_FILECON_SYMLINK:
+				fprintf(out, "%s ", CIL_KEY_SYMLINK);
+				break;
+			default:
+				fprintf(out, "<?FILETYPE> ");
+			}
+		}
 		if (genfscon->context)
 			write_context(out, genfscon->context, CIL_TRUE);
 		else
diff --git a/libsepol/fuzz/binpolicy-fuzzer.c b/libsepol/fuzz/binpolicy-fuzzer.c
new file mode 100644
index 0000000..85c5964
--- /dev/null
+++ b/libsepol/fuzz/binpolicy-fuzzer.c
@@ -0,0 +1,63 @@
+#include <sepol/debug.h>
+#include <sepol/kernel_to_cil.h>
+#include <sepol/kernel_to_conf.h>
+#include <sepol/policydb/policydb.h>
+
+extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int write_binary_policy(policydb_t *p, FILE *outfp)
+{
+	struct policy_file pf;
+
+	policy_file_init(&pf);
+	pf.type = PF_USE_STDIO;
+	pf.fp = outfp;
+	return policydb_write(p, &pf);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+	policydb_t policydb = {};
+	sidtab_t sidtab = {};
+	struct policy_file pf;
+	FILE *devnull = NULL;
+
+	sepol_debug(0);
+
+	policy_file_init(&pf);
+	pf.type = PF_USE_MEMORY;
+	pf.data = (char *) data;
+	pf.len = size;
+
+	if (policydb_init(&policydb))
+		goto exit;
+
+	if (policydb_read(&policydb, &pf, /*verbose=*/0))
+		goto exit;
+
+	if (policydb_load_isids(&policydb, &sidtab))
+		goto exit;
+
+	if (policydb.policy_type == POLICY_KERN)
+		(void) policydb_optimize(&policydb);
+
+	devnull = fopen("/dev/null", "w");
+	if (!devnull)
+		goto exit;
+
+	(void) write_binary_policy(&policydb, devnull);
+
+	(void) sepol_kernel_policydb_to_conf(devnull, &policydb);
+
+	(void) sepol_kernel_policydb_to_cil(devnull, &policydb);
+
+exit:
+	if (devnull != NULL)
+		fclose(devnull);
+
+	policydb_destroy(&policydb);
+	sepol_sidtab_destroy(&sidtab);
+
+	/* Non-zero return values are reserved for future use. */
+	return 0;
+}
diff --git a/libsepol/fuzz/policy.bin b/libsepol/fuzz/policy.bin
new file mode 100644
index 0000000..6f977ef
--- /dev/null
+++ b/libsepol/fuzz/policy.bin
Binary files differ
diff --git a/libsepol/fuzz/secilc-fuzzer.c b/libsepol/fuzz/secilc-fuzzer.c
index 255b324..9a1a16d 100644
--- a/libsepol/fuzz/secilc-fuzzer.c
+++ b/libsepol/fuzz/secilc-fuzzer.c
@@ -8,6 +8,10 @@
 #include <sepol/cil/cil.h>
 #include <sepol/policydb.h>
 
+static void log_handler(__attribute__((unused)) int lvl, __attribute__((unused)) const char *msg) {
+	/* be quiet */
+}
+
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 	enum cil_log_level log_level = CIL_ERR;
 	struct sepol_policy_file *pf = NULL;
@@ -24,6 +28,7 @@
 	sepol_policydb_t *pdb = NULL;
 
 	cil_set_log_level(log_level);
+	cil_set_log_handler(log_handler);
 
 	cil_db_init(&db);
 	cil_set_disable_dontaudit(db, disable_dontaudit);
diff --git a/libsepol/include/sepol/policydb/polcaps.h b/libsepol/include/sepol/policydb/polcaps.h
index 40669fb..f5e32e6 100644
--- a/libsepol/include/sepol/policydb/polcaps.h
+++ b/libsepol/include/sepol/policydb/polcaps.h
@@ -7,16 +7,17 @@
 
 /* Policy capabilities */
 enum {
-	POLICYDB_CAPABILITY_NETPEER,
-	POLICYDB_CAPABILITY_OPENPERM,
-	POLICYDB_CAPABILITY_EXTSOCKCLASS,
-	POLICYDB_CAPABILITY_ALWAYSNETWORK,
-	POLICYDB_CAPABILITY_CGROUPSECLABEL,
-	POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION,
-	POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS,
-	__POLICYDB_CAPABILITY_MAX
+	POLICYDB_CAP_NETPEER,
+	POLICYDB_CAP_OPENPERM,
+	POLICYDB_CAP_EXTSOCKCLASS,
+	POLICYDB_CAP_ALWAYSNETWORK,
+	POLICYDB_CAP_CGROUPSECLABEL,
+	POLICYDB_CAP_NNP_NOSUID_TRANSITION,
+	POLICYDB_CAP_GENFS_SECLABEL_SYMLINKS,
+	POLICYDB_CAP_IOCTL_SKIP_CLOEXEC,
+	__POLICYDB_CAP_MAX
 };
-#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
+#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
 
 /* Convert a capability name to number. */
 extern int sepol_polcap_getnum(const char *name);
diff --git a/libsepol/src/Makefile b/libsepol/src/Makefile
index dc8b177..13410c6 100644
--- a/libsepol/src/Makefile
+++ b/libsepol/src/Makefile
@@ -29,6 +29,12 @@
 override CFLAGS += -I$(CILDIR)/include
 endif
 
+# check for reallocarray(3) availability
+H := \#
+ifeq (yes,$(shell printf '${H}define _GNU_SOURCE\n${H}include <stdlib.h>\nint main(void){void*p=reallocarray(NULL, 1, sizeof(char));return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
+override CFLAGS += -DHAVE_REALLOCARRAY
+endif
+
 LD_SONAME_FLAGS=-soname,$(LIBSO),--version-script=$(LIBMAP),-z,defs
 
 LN=ln
diff --git a/libsepol/src/assertion.c b/libsepol/src/assertion.c
index dd2749a..161874c 100644
--- a/libsepol/src/assertion.c
+++ b/libsepol/src/assertion.c
@@ -36,13 +36,21 @@
 	unsigned long errors;
 };
 
+static const char* policy_name(policydb_t *p) {
+	const char *policy_file = "policy.conf";
+	if (p->name) {
+		policy_file = p->name;
+	}
+	return policy_file;
+}
+
 static void report_failure(sepol_handle_t *handle, policydb_t *p, const avrule_t *avrule,
 			   unsigned int stype, unsigned int ttype,
 			   const class_perm_node_t *curperm, uint32_t perms)
 {
 	if (avrule->source_filename) {
-		ERR(handle, "neverallow on line %lu of %s (or line %lu of policy.conf) violated by allow %s %s:%s {%s };",
-		    avrule->source_line, avrule->source_filename, avrule->line,
+		ERR(handle, "neverallow on line %lu of %s (or line %lu of %s) violated by allow %s %s:%s {%s };",
+		    avrule->source_line, avrule->source_filename, avrule->line, policy_name(p),
 		    p->p_type_val_to_name[stype],
 		    p->p_type_val_to_name[ttype],
 		    p->p_class_val_to_name[curperm->tclass - 1],
@@ -65,14 +73,11 @@
 static int match_any_class_permissions(class_perm_node_t *cp, uint32_t class, uint32_t data)
 {
 	for (; cp; cp = cp->next) {
-		if ((cp->tclass == class) && (cp->data & data)) {
-			break;
-		}
+		if ((cp->tclass == class) && (cp->data & data))
+			return 1;
 	}
-	if (!cp)
-		return 0;
 
-	return 1;
+	return 0;
 }
 
 static int extended_permissions_and(uint32_t *perms1, uint32_t *perms2) {
@@ -151,15 +156,16 @@
 	ebitmap_t *tattr = &p->type_attr_map[ttype];
 	ebitmap_node_t *snode, *tnode;
 	unsigned int i, j;
-	int rc = 1;
-	int ret = 0;
+	int rc;
+	int found_xperm = 0;
+	int errors = 0;
 
 	memcpy(&tmp_key, k, sizeof(avtab_key_t));
 	tmp_key.specified = AVTAB_XPERMS_ALLOWED;
 
 	ebitmap_for_each_positive_bit(sattr, snode, i) {
+		tmp_key.source_type = i + 1;
 		ebitmap_for_each_positive_bit(tattr, tnode, j) {
-			tmp_key.source_type = i + 1;
 			tmp_key.target_type = j + 1;
 			for (node = avtab_search_node(avtab, &tmp_key);
 			     node;
@@ -168,40 +174,39 @@
 				if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
 						&& (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER))
 					continue;
-
+				found_xperm = 1;
 				rc = check_extended_permissions(avrule->xperms, xperms);
 				/* failure on the extended permission check_extended_permissions */
 				if (rc) {
 					extended_permissions_violated(&error, avrule->xperms, xperms);
-					ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of policy.conf) violated by\n"
+					ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of %s) violated by\n"
 							"allowxperm %s %s:%s %s;",
-							avrule->source_line, avrule->source_filename, avrule->line,
+							avrule->source_line, avrule->source_filename, avrule->line, policy_name(p),
 							p->p_type_val_to_name[i],
 							p->p_type_val_to_name[j],
 							p->p_class_val_to_name[curperm->tclass - 1],
 							sepol_extended_perms_to_string(&error));
 
-					rc = 0;
-					ret++;
+					errors++;
 				}
 			}
 		}
 	}
 
 	/* failure on the regular permissions */
-	if (rc) {
-		ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of policy.conf) violated by\n"
+	if (!found_xperm) {
+		ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of %s) violated by\n"
 				"allow %s %s:%s {%s };",
-				avrule->source_line, avrule->source_filename, avrule->line,
+				avrule->source_line, avrule->source_filename, avrule->line, policy_name(p),
 				p->p_type_val_to_name[stype],
 				p->p_type_val_to_name[ttype],
 				p->p_class_val_to_name[curperm->tclass - 1],
 				sepol_av_to_string(p, curperm->tclass, perms));
-		ret++;
+		errors++;
 
 	}
 
-	return ret;
+	return errors;
 }
 
 static int report_assertion_avtab_matches(avtab_key_t *k, avtab_datum_t *d, void *args)
@@ -214,9 +219,10 @@
 	avrule_t *avrule = a->avrule;
 	class_perm_node_t *cp;
 	uint32_t perms;
-	ebitmap_t src_matches, tgt_matches, self_matches, matches;
+	ebitmap_t src_matches, tgt_matches, self_matches;
 	ebitmap_node_t *snode, *tnode;
 	unsigned int i, j;
+	const int is_avrule_self = (avrule->flags & RULE_SELF) != 0;
 
 	if ((k->specified & AVTAB_ALLOWED) == 0)
 		return 0;
@@ -227,31 +233,27 @@
 	ebitmap_init(&src_matches);
 	ebitmap_init(&tgt_matches);
 	ebitmap_init(&self_matches);
-	ebitmap_init(&matches);
 
 	rc = ebitmap_and(&src_matches, &avrule->stypes.types,
 			 &p->attr_type_map[k->source_type - 1]);
-	if (rc)
+	if (rc < 0)
 		goto oom;
 
 	if (ebitmap_is_empty(&src_matches))
 		goto exit;
 
 	rc = ebitmap_and(&tgt_matches, &avrule->ttypes.types, &p->attr_type_map[k->target_type -1]);
-	if (rc)
+	if (rc < 0)
 		goto oom;
 
-	if (avrule->flags == RULE_SELF) {
-		rc = ebitmap_and(&matches, &p->attr_type_map[k->source_type - 1], &p->attr_type_map[k->target_type - 1]);
-		if (rc)
-			goto oom;
-		rc = ebitmap_and(&self_matches, &avrule->stypes.types, &matches);
-		if (rc)
+	if (is_avrule_self) {
+		rc = ebitmap_and(&self_matches, &src_matches, &p->attr_type_map[k->target_type - 1]);
+		if (rc < 0)
 			goto oom;
 
 		if (!ebitmap_is_empty(&self_matches)) {
 			rc = ebitmap_union(&tgt_matches, &self_matches);
-			if (rc)
+			if (rc < 0)
 				goto oom;
 		}
 	}
@@ -268,6 +270,8 @@
 
 		ebitmap_for_each_positive_bit(&src_matches, snode, i) {
 			ebitmap_for_each_positive_bit(&tgt_matches, tnode, j) {
+				if (is_avrule_self && i != j)
+					continue;
 				if (avrule->specified == AVRULE_XPERMS_NEVERALLOW) {
 					a->errors += report_assertion_extended_permissions(handle,p, avrule,
 											i, j, cp, perms, k, avtab);
@@ -278,16 +282,12 @@
 			}
 		}
 	}
-	goto exit;
 
 oom:
-	ERR(NULL, "Out of memory - unable to check neverallows");
-
 exit:
 	ebitmap_destroy(&src_matches);
 	ebitmap_destroy(&tgt_matches);
 	ebitmap_destroy(&self_matches);
-	ebitmap_destroy(&matches);
 	return rc;
 }
 
@@ -301,12 +301,14 @@
 	args.avrule = avrule;
 	args.errors = 0;
 
+	args.avtab =  &p->te_avtab;
 	rc = avtab_map(&p->te_avtab, report_assertion_avtab_matches, &args);
-	if (rc)
+	if (rc < 0)
 		goto oom;
 
+	args.avtab =  &p->te_cond_avtab;
 	rc = avtab_map(&p->te_cond_avtab, report_assertion_avtab_matches, &args);
-	if (rc)
+	if (rc < 0)
 		goto oom;
 
 	return args.errors;
@@ -337,8 +339,8 @@
 	tmp_key.specified = AVTAB_XPERMS_ALLOWED;
 
 	ebitmap_for_each_positive_bit(sattr, snode, i) {
+		tmp_key.source_type = i + 1;
 		ebitmap_for_each_positive_bit(tattr, tnode, j) {
-			tmp_key.source_type = i + 1;
 			tmp_key.target_type = j + 1;
 			for (node = avtab_search_node(avtab, &tmp_key);
 			     node;
@@ -350,7 +352,7 @@
 					continue;
 				rc = check_extended_permissions(neverallow_xperms, xperms);
 				if (rc)
-					break;
+					return rc;
 			}
 		}
 	}
@@ -377,125 +379,138 @@
 static int check_assertion_extended_permissions(avrule_t *avrule, avtab_t *avtab,
 						avtab_key_t *k, policydb_t *p)
 {
-	ebitmap_t src_matches, tgt_matches, self_matches, matches;
+	ebitmap_t src_matches, tgt_matches, self_matches;
 	unsigned int i, j;
 	ebitmap_node_t *snode, *tnode;
-	class_perm_node_t *cp;
+	const int is_avrule_self = (avrule->flags & RULE_SELF) != 0;
 	int rc;
-	int ret = 1;
 
 	ebitmap_init(&src_matches);
 	ebitmap_init(&tgt_matches);
 	ebitmap_init(&self_matches);
-	ebitmap_init(&matches);
 
 	rc = ebitmap_and(&src_matches, &avrule->stypes.types,
 			 &p->attr_type_map[k->source_type - 1]);
-	if (rc)
+	if (rc < 0)
 		goto oom;
 
-	if (ebitmap_is_empty(&src_matches))
+	if (ebitmap_is_empty(&src_matches)) {
+		rc = 0;
 		goto exit;
+	}
 
 	rc = ebitmap_and(&tgt_matches, &avrule->ttypes.types,
 			 &p->attr_type_map[k->target_type -1]);
-	if (rc)
+	if (rc < 0)
 		goto oom;
 
-	if (avrule->flags == RULE_SELF) {
-		rc = ebitmap_and(&matches, &p->attr_type_map[k->source_type - 1],
-				&p->attr_type_map[k->target_type - 1]);
-		if (rc)
-			goto oom;
-		rc = ebitmap_and(&self_matches, &avrule->stypes.types, &matches);
-		if (rc)
+	if (is_avrule_self) {
+		rc = ebitmap_and(&self_matches, &src_matches, &p->attr_type_map[k->target_type - 1]);
+		if (rc < 0)
 			goto oom;
 
 		if (!ebitmap_is_empty(&self_matches)) {
 			rc = ebitmap_union(&tgt_matches, &self_matches);
-			if (rc)
+			if (rc < 0)
 				goto oom;
 		}
 	}
 
-	if (ebitmap_is_empty(&tgt_matches))
+	if (ebitmap_is_empty(&tgt_matches)) {
+		rc = 0;
 		goto exit;
+	}
 
-	for (cp = avrule->perms; cp; cp = cp->next) {
-		if (cp->tclass != k->target_class)
-			continue;
-		ebitmap_for_each_positive_bit(&src_matches, snode, i) {
-			ebitmap_for_each_positive_bit(&tgt_matches, tnode, j) {
-				ret = check_assertion_extended_permissions_avtab(
-						avrule, avtab, i, j, k, p);
-				if (ret)
-					goto exit;
+	ebitmap_for_each_positive_bit(&src_matches, snode, i) {
+		ebitmap_for_each_positive_bit(&tgt_matches, tnode, j) {
+			if (is_avrule_self && i != j)
+				continue;
+			if (check_assertion_extended_permissions_avtab(avrule, avtab, i, j, k, p)) {
+				rc = 1;
+				goto exit;
 			}
 		}
 	}
-	goto exit;
+
+	rc = 0;
 
 oom:
-	ERR(NULL, "Out of memory - unable to check neverallows");
-
 exit:
 	ebitmap_destroy(&src_matches);
 	ebitmap_destroy(&tgt_matches);
-	ebitmap_destroy(&matches);
-	return ret;
+	ebitmap_destroy(&self_matches);
+	return rc;
+}
+
+static int check_assertion_self_match(avtab_key_t *k, avrule_t *avrule, policydb_t *p)
+{
+	ebitmap_t src_matches;
+	int rc;
+
+	/* The key's target must match something in the matches of the avrule's source
+	 * and the key's source.
+	 */
+
+	rc = ebitmap_and(&src_matches, &avrule->stypes.types, &p->attr_type_map[k->source_type - 1]);
+	if (rc < 0)
+		goto oom;
+
+	if (!ebitmap_match_any(&src_matches, &p->attr_type_map[k->target_type - 1])) {
+		rc = 0;
+		goto nomatch;
+	}
+
+	rc = 1;
+
+oom:
+nomatch:
+	ebitmap_destroy(&src_matches);
+	return rc;
 }
 
 static int check_assertion_avtab_match(avtab_key_t *k, avtab_datum_t *d, void *args)
 {
-	int rc, rc2 = 0;
+	int rc;
 	struct avtab_match_args *a = (struct avtab_match_args *)args;
 	policydb_t *p = a->p;
 	avrule_t *avrule = a->avrule;
 	avtab_t *avtab = a->avtab;
 
 	if ((k->specified & AVTAB_ALLOWED) == 0)
-		goto exit;
+		goto nomatch;
 
 	if (!match_any_class_permissions(avrule->perms, k->target_class, d->data))
-		goto exit;
+		goto nomatch;
 
-	rc = ebitmap_match_any(&avrule->stypes.types, &p->attr_type_map[k->source_type - 1]);
-	if (rc == 0)
-		goto exit;
-
-	if (avrule->flags == RULE_SELF) {
-		/* If the neverallow uses SELF, then it is not enough that the
-		 * neverallow's source matches the src and tgt of the rule being checked.
-		 * It must match the same thing in the src and tgt, so AND the source
-		 * and target together and check for a match on the result.
-		 */
-		ebitmap_t match;
-		rc = ebitmap_and(&match, &p->attr_type_map[k->source_type - 1], &p->attr_type_map[k->target_type - 1] );
-		if (rc) {
-			ebitmap_destroy(&match);
-			goto oom;
-		}
-		rc2 = ebitmap_match_any(&avrule->stypes.types, &match);
-		ebitmap_destroy(&match);
-	}
+	if (!ebitmap_match_any(&avrule->stypes.types, &p->attr_type_map[k->source_type - 1]))
+		goto nomatch;
 
 	/* neverallow may have tgts even if it uses SELF */
-	rc = ebitmap_match_any(&avrule->ttypes.types, &p->attr_type_map[k->target_type -1]);
-	if (rc == 0 && rc2 == 0)
-		goto exit;
+	if (!ebitmap_match_any(&avrule->ttypes.types, &p->attr_type_map[k->target_type -1])) {
+		if (avrule->flags == RULE_SELF) {
+			rc = check_assertion_self_match(k, avrule, p);
+			if (rc < 0)
+				goto oom;
+			if (rc == 0)
+				goto nomatch;
+		} else {
+			goto nomatch;
+		}
+	}
 
 	if (avrule->specified == AVRULE_XPERMS_NEVERALLOW) {
 		rc = check_assertion_extended_permissions(avrule, avtab, k, p);
+		if (rc < 0)
+			goto oom;
 		if (rc == 0)
-			goto exit;
+			goto nomatch;
 	}
 	return 1;
 
-exit:
+nomatch:
 	return 0;
 
 oom:
-	ERR(NULL, "Out of memory - unable to check neverallows");
 	return rc;
 }
 
@@ -538,6 +553,10 @@
 		if (!(a->specified & (AVRULE_NEVERALLOW | AVRULE_XPERMS_NEVERALLOW)))
 			continue;
 		rc = check_assertion(p, a);
+		if (rc < 0) {
+			ERR(handle, "Error occurred while checking neverallows");
+			return -1;
+		}
 		if (rc) {
 			rc = report_assertion_failures(handle, p, a);
 			if (rc < 0) {
diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c
index 46e1e75..7920b60 100644
--- a/libsepol/src/avtab.c
+++ b/libsepol/src/avtab.c
@@ -503,6 +503,11 @@
 
 		for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
 			if (val & spec_order[i]) {
+				if (items >= items2) { /* items is index, items2 is total number */
+					ERR(fp->handle, "entry has too many items (%d/%d)",
+					    items + 1, items2);
+					return -1;
+				}
 				key.specified = spec_order[i] | enabled;
 				datum.data = le32_to_cpu(buf32[items++]);
 				rc = insertf(a, &key, &datum, p);
@@ -543,7 +548,7 @@
 	if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) &&
 			(key.specified & AVTAB_XPERMS)) {
 		ERR(fp->handle, "policy version %u does not support extended "
-				"permissions rules and one was specified\n", vers);
+				"permissions rules and one was specified", vers);
 		return -1;
 	} else if (key.specified & AVTAB_XPERMS) {
 		rc = next_entry(&buf8, fp, sizeof(uint8_t));
diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
index 037dc7e..f78b38a 100644
--- a/libsepol/src/conditional.c
+++ b/libsepol/src/conditional.c
@@ -25,6 +25,7 @@
 #include <sepol/policydb/conditional.h>
 
 #include "private.h"
+#include "debug.h"
 
 /* move all type rules to top of t/f lists to help kernel on evaluation */
 static void cond_optimize(cond_av_list_t ** l)
@@ -314,8 +315,7 @@
 	if (new_state != node->cur_state) {
 		node->cur_state = new_state;
 		if (new_state == -1)
-			printf
-			    ("expression result was undefined - disabling all rules.\n");
+			WARN(NULL, "expression result was undefined - disabling all rules.");
 		/* turn the rules on or off */
 		for (cur = node->true_list; cur != NULL; cur = cur->next) {
 			if (new_state <= 0) {
@@ -368,8 +368,7 @@
 		if (ne) {
 			ne->next = NULL;
 		} else {	/* ne should never be NULL */
-			printf
-			    ("Found expr with no bools and only a ! - this should never happen.\n");
+			ERR(NULL, "Found expr with no bools and only a ! - this should never happen.");
 			return -1;
 		}
 		/* swap the true and false lists */
@@ -421,9 +420,8 @@
 			}
 			k = cond_evaluate_expr(p, cn->expr);
 			if (k == -1) {
-				printf
-				    ("While testing expression, expression result "
-				     "was undefined - this should never happen.\n");
+				ERR(NULL, "While testing expression, expression result "
+				     "was undefined - this should never happen.");
 				return -1;
 			}
 			/* set the bit if expression evaluates true */
@@ -524,7 +522,7 @@
 	if (p->bool_val_to_struct)
 		free(p->bool_val_to_struct);
 	p->bool_val_to_struct = (cond_bool_datum_t **)
-	    malloc(p->p_bools.nprim * sizeof(cond_bool_datum_t *));
+	    mallocarray(p->p_bools.nprim, sizeof(cond_bool_datum_t *));
 	if (!p->bool_val_to_struct)
 		return -1;
 	return 0;
@@ -635,9 +633,8 @@
 	 */
 	if (k->specified & AVTAB_TYPE) {
 		if (avtab_search(&p->te_avtab, k)) {
-			printf
-			    ("security: type rule already exists outside of a conditional.");
-			goto err;
+			WARN(NULL, "security: type rule already exists outside of a conditional.");
+			return -1;
 		}
 		/*
 		 * If we are reading the false list other will be a pointer to
@@ -652,9 +649,8 @@
 			if (node_ptr) {
 				if (avtab_search_node_next
 				    (node_ptr, k->specified)) {
-					printf
-					    ("security: too many conflicting type rules.");
-					goto err;
+					ERR(NULL, "security: too many conflicting type rules.");
+					return -1;
 				}
 				found = 0;
 				for (cur = other; cur != NULL; cur = cur->next) {
@@ -664,30 +660,28 @@
 					}
 				}
 				if (!found) {
-					printf
-					    ("security: conflicting type rules.\n");
-					goto err;
+					ERR(NULL, "security: conflicting type rules.");
+					return -1;
 				}
 			}
 		} else {
 			if (avtab_search(&p->te_cond_avtab, k)) {
-				printf
-				    ("security: conflicting type rules when adding type rule for true.\n");
-				goto err;
+				ERR(NULL, "security: conflicting type rules when adding type rule for true.");
+				return -1;
 			}
 		}
 	}
 
 	node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
 	if (!node_ptr) {
-		printf("security: could not insert rule.");
-		goto err;
+		ERR(NULL, "security: could not insert rule.");
+		return -1;
 	}
 	node_ptr->parse_context = (void *)1;
 
 	list = malloc(sizeof(cond_av_list_t));
 	if (!list)
-		goto err;
+		return -1;
 	memset(list, 0, sizeof(cond_av_list_t));
 
 	list->node = node_ptr;
@@ -697,11 +691,6 @@
 		data->tail->next = list;
 	data->tail = list;
 	return 0;
-
-      err:
-	cond_av_list_destroy(data->head);
-	data->head = NULL;
-	return -1;
 }
 
 static int cond_read_av_list(policydb_t * p, void *fp,
@@ -730,8 +719,10 @@
 	for (i = 0; i < len; i++) {
 		rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab,
 				     cond_insertf, &data);
-		if (rc)
+		if (rc) {
+			cond_av_list_destroy(data.head);
 			return rc;
+		}
 
 	}
 
@@ -742,14 +733,12 @@
 static int expr_isvalid(policydb_t * p, cond_expr_t * expr)
 {
 	if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {
-		printf
-		    ("security: conditional expressions uses unknown operator.\n");
+		WARN(NULL, "security: conditional expressions uses unknown operator.");
 		return 0;
 	}
 
 	if (expr->bool > p->p_bools.nprim) {
-		printf
-		    ("security: conditional expressions uses unknown bool.\n");
+		WARN(NULL, "security: conditional expressions uses unknown bool.");
 		return 0;
 	}
 	return 1;
diff --git a/libsepol/src/context_record.c b/libsepol/src/context_record.c
index 435f788..2bda121 100644
--- a/libsepol/src/context_record.c
+++ b/libsepol/src/context_record.c
@@ -127,7 +127,7 @@
 	    (sepol_context_t *) malloc(sizeof(sepol_context_t));
 
 	if (!con) {
-		ERR(handle, "out of memory, could not " "create context\n");
+		ERR(handle, "out of memory, could not create context");
 		return STATUS_ERR;
 	}
 
diff --git a/libsepol/src/ebitmap.c b/libsepol/src/ebitmap.c
index 1de3816..bd98c0f 100644
--- a/libsepol/src/ebitmap.c
+++ b/libsepol/src/ebitmap.c
@@ -406,8 +406,7 @@
 	count = le32_to_cpu(buf[2]);
 
 	if (mapsize != MAPSIZE) {
-		printf
-		    ("security: ebitmap: map size %d does not match my size %zu (high bit was %d)\n",
+		ERR(NULL, "security: ebitmap: map size %d does not match my size %zu (high bit was %d)",
 		     mapsize, MAPSIZE, e->highbit);
 		goto bad;
 	}
@@ -416,8 +415,7 @@
 		goto ok;
 	}
 	if (e->highbit & (MAPSIZE - 1)) {
-		printf
-		    ("security: ebitmap: high bit (%d) is not a multiple of the map size (%zu)\n",
+		ERR(NULL, "security: ebitmap: high bit (%d) is not a multiple of the map size (%zu)",
 		     e->highbit, MAPSIZE);
 		goto bad;
 	}
@@ -429,12 +427,12 @@
 	for (i = 0; i < count; i++) {
 		rc = next_entry(buf, fp, sizeof(uint32_t));
 		if (rc < 0) {
-			printf("security: ebitmap: truncated map\n");
+			ERR(NULL, "security: ebitmap: truncated map");
 			goto bad;
 		}
 		n = (ebitmap_node_t *) malloc(sizeof(ebitmap_node_t));
 		if (!n) {
-			printf("security: ebitmap: out of memory\n");
+			ERR(NULL, "security: ebitmap: out of memory");
 			rc = -ENOMEM;
 			goto bad;
 		}
@@ -443,34 +441,30 @@
 		n->startbit = le32_to_cpu(buf[0]);
 
 		if (n->startbit & (MAPSIZE - 1)) {
-			printf
-			    ("security: ebitmap start bit (%d) is not a multiple of the map size (%zu)\n",
+			ERR(NULL, "security: ebitmap start bit (%d) is not a multiple of the map size (%zu)",
 			     n->startbit, MAPSIZE);
 			goto bad_free;
 		}
 		if (n->startbit > (e->highbit - MAPSIZE)) {
-			printf
-			    ("security: ebitmap start bit (%d) is beyond the end of the bitmap (%zu)\n",
+			ERR(NULL, "security: ebitmap start bit (%d) is beyond the end of the bitmap (%zu)",
 			     n->startbit, (e->highbit - MAPSIZE));
 			goto bad_free;
 		}
 		rc = next_entry(&map, fp, sizeof(uint64_t));
 		if (rc < 0) {
-			printf("security: ebitmap: truncated map\n");
+			ERR(NULL, "security: ebitmap: truncated map");
 			goto bad_free;
 		}
 		n->map = le64_to_cpu(map);
 
 		if (!n->map) {
-			printf
-			    ("security: ebitmap: null map in ebitmap (startbit %d)\n",
+			ERR(NULL, "security: ebitmap: null map in ebitmap (startbit %d)",
 			     n->startbit);
 			goto bad_free;
 		}
 		if (l) {
 			if (n->startbit <= l->startbit) {
-				printf
-				    ("security: ebitmap: start bit %d comes after start bit %d\n",
+				ERR(NULL, "security: ebitmap: start bit %d comes after start bit %d",
 				     n->startbit, l->startbit);
 				goto bad_free;
 			}
@@ -481,8 +475,7 @@
 		l = n;
 	}
 	if (count && l->startbit + MAPSIZE != e->highbit) {
-		printf
-		    ("security: ebitmap: high bit %u has not the expected value %zu\n",
+		ERR(NULL, "security: ebitmap: high bit %u has not the expected value %zu",
 		     e->highbit, l->startbit + MAPSIZE);
 		goto bad;
 	}
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index a6a466f..7da51a4 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -166,7 +166,7 @@
 
 	if (new_type->flags & TYPE_FLAGS_PERMISSIVE)
 		if (ebitmap_set_bit(&state->out->permissive_map, new_type->s.value, 1)) {
-			ERR(state->handle, "Out of memory!\n");
+			ERR(state->handle, "Out of memory!");
 			return -1;
 		}
 
@@ -929,11 +929,15 @@
 	if (!sl->sens)
 		return 0;
 
+	/* Invalid sensitivity */
+	if (sl->sens > p->p_levels.nprim || !p->p_sens_val_to_name[sl->sens - 1])
+		return -1;
+
 	l->sens = sl->sens;
 	levdatum = (level_datum_t *) hashtab_search(p->p_levels.table,
 						    p->p_sens_val_to_name[l->sens - 1]);
 	if (!levdatum) {
-		ERR(h, "%s: Impossible situation found, nothing in p_levels.table.\n",
+		ERR(h, "%s: Impossible situation found, nothing in p_levels.table.",
 		    __func__);
 		errno = ENOENT;
 		return -1;
@@ -1690,7 +1694,7 @@
 	uint32_t oldtype = 0;
 
 	if (!(specified & (AVRULE_TRANSITION|AVRULE_MEMBER|AVRULE_CHANGE))) {
-		ERR(handle, "Invalid specification: %"PRIu32"\n", specified);
+		ERR(handle, "Invalid specification: %"PRIu32, specified);
 		return EXPAND_RULE_ERROR;
 	}
 
@@ -1869,7 +1873,7 @@
 				return EXPAND_RULE_ERROR;
 			break;
 		default:
-			ERR(handle, "Unknown specification: %"PRIu32"\n", specified);
+			ERR(handle, "Unknown specification: %"PRIu32, specified);
 			return EXPAND_RULE_ERROR;
 		}
 
@@ -2477,7 +2481,7 @@
 
 	/* if role is to be complimented, invert the entire bitmap here */
 	if (x->flags & ROLE_COMP) {
-		for (i = 0; i < ebitmap_length(r); i++) {
+		for (i = 0; i < p->p_roles.nprim; i++) {
 			if (ebitmap_get_bit(r, i)) {
 				if (ebitmap_set_bit(r, i, 0))
 					return -1;
@@ -2966,6 +2970,9 @@
 
 	state.out->policy_type = POLICY_KERN;
 	state.out->policyvers = POLICYDB_VERSION_MAX;
+	if (state.base->name) {
+		state.out->name = strdup(state.base->name);
+	}
 
 	/* Copy mls state from base to out */
 	out->mls = base->mls;
@@ -3146,9 +3153,9 @@
 		goto cleanup;
 
 	/* Build the type<->attribute maps and remove attributes. */
-	state.out->attr_type_map = malloc(state.out->p_types.nprim *
+	state.out->attr_type_map = mallocarray(state.out->p_types.nprim,
 					  sizeof(ebitmap_t));
-	state.out->type_attr_map = malloc(state.out->p_types.nprim *
+	state.out->type_attr_map = mallocarray(state.out->p_types.nprim,
 					  sizeof(ebitmap_t));
 	if (!state.out->attr_type_map || !state.out->type_attr_map) {
 		ERR(handle, "Out of memory!");
diff --git a/libsepol/src/hashtab.c b/libsepol/src/hashtab.c
index 21143b7..3ecaf16 100644
--- a/libsepol/src/hashtab.c
+++ b/libsepol/src/hashtab.c
@@ -32,6 +32,8 @@
 #include <string.h>
 #include <sepol/policydb/hashtab.h>
 
+#include "private.h"
+
 hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
 						     const_hashtab_key_t key),
 			 int (*keycmp) (hashtab_t h,
@@ -52,7 +54,7 @@
 	p->nel = 0;
 	p->hash_value = hash_value;
 	p->keycmp = keycmp;
-	p->htable = (hashtab_ptr_t *) malloc(sizeof(hashtab_ptr_t) * size);
+	p->htable = (hashtab_ptr_t *) mallocarray(size, sizeof(hashtab_ptr_t));
 	if (p->htable == NULL) {
 		free(p);
 		return NULL;
@@ -222,8 +224,9 @@
 		int (*apply) (hashtab_key_t k,
 			      hashtab_datum_t d, void *args), void *args)
 {
-	unsigned int i, ret;
+	unsigned int i;
 	hashtab_ptr_t cur;
+	int ret;
 
 	if (!h)
 		return SEPOL_OK;
diff --git a/libsepol/src/hierarchy.c b/libsepol/src/hierarchy.c
index 8919daa..350443a 100644
--- a/libsepol/src/hierarchy.c
+++ b/libsepol/src/hierarchy.c
@@ -237,7 +237,7 @@
 	ERR(handle, "Insufficient memory");
 
 exit:
-	ERR(handle,"Failed to expand parent rules\n");
+	ERR(handle,"Failed to expand parent rules");
 	avtab_destroy(global_avtab);
 	bounds_destroy_cond_info(*cond_info);
 	*cond_info = NULL;
diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index 305567a..869f694 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -278,10 +278,13 @@
 	char *expr = NULL;
 	int is_mls;
 	char *perms;
-	const char *format_str;
+	const char *key_word;
 	struct strs *strs;
 
 	for (curr = constraint_rules; curr != NULL; curr = curr->next) {
+		if (curr->permissions == 0) {
+			continue;
+		}
 		expr = constraint_expr_to_str(pdb, curr->expr, &is_mls);
 		if (!expr) {
 			rc = -1;
@@ -291,14 +294,14 @@
 		perms = sepol_av_to_string(pdb, class->s.value, curr->permissions);
 
 		if (is_mls) {
-			format_str = "(mlsconstrain (%s (%s)) %s)";
+			key_word = "mlsconstrain";
 			strs = mls_list;
 		} else {
-			format_str = "(constrain (%s (%s)) %s)";
+			key_word = "constrain";
 			strs = non_mls_list;
 		}
 
-		rc = strs_create_and_add(strs, format_str, 3, classkey, perms+1, expr);
+		rc = strs_create_and_add(strs, "(%s (%s (%s)) %s)", 4, key_word, classkey, perms+1, expr);
 		free(expr);
 		if (rc != 0) {
 			goto exit;
@@ -319,7 +322,7 @@
 	struct constraint_node *curr;
 	char *expr = NULL;
 	int is_mls;
-	const char *format_str;
+	const char *key_word;
 	struct strs *strs;
 	int rc = 0;
 
@@ -331,14 +334,14 @@
 		}
 
 		if (is_mls) {
-			format_str = "(mlsvalidatetrans %s %s)";
+			key_word = "mlsvalidatetrans";
 			strs = mls_list;
 		} else {
-			format_str = "(validatetrans %s %s)";
+			key_word = "validatetrans";
 			strs = non_mls_list;
 		}
 
-		rc = strs_create_and_add(strs, format_str, 2, classkey, expr);
+		rc = strs_create_and_add(strs, "(%s %s %s)", 3, key_word, classkey, expr);
 		free(expr);
 		if (rc != 0) {
 			goto exit;
@@ -358,6 +361,7 @@
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->constraints) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs);
@@ -383,6 +387,7 @@
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->validatetrans) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs);
@@ -461,6 +466,7 @@
 	/* class */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		perms = class_or_common_perms_to_str(&class->permissions);
 		if (perms) {
@@ -488,6 +494,7 @@
 	/* classcommon */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		if (class->comkey != NULL) {
 			sepol_printf(out, "(classcommon %s %s)\n", name, class->comkey);
@@ -503,6 +510,7 @@
 	}
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = class->comkey;
 		if (name != NULL) {
 			common = hashtab_search(pdb->p_commons.table, name);
@@ -727,6 +735,7 @@
 	/* default_user */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_user != 0) {
 			rc = write_default_user_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -738,6 +747,7 @@
 	/* default_role */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_role != 0) {
 			rc = write_default_role_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -749,6 +759,7 @@
 	/* default_type */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_type != 0) {
 			rc = write_default_type_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -764,6 +775,7 @@
 	/* default_range */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_range) {
 			rc = write_default_range_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -1035,7 +1047,6 @@
 	struct ebitmap_node *node;
 	uint32_t i, start, range;
 	char *catsbuf = NULL, *p;
-	const char *fmt;
 	int len, remaining;
 
 	remaining = (int)cats_ebitmap_len(cats, val_to_name);
@@ -1063,9 +1074,15 @@
 			continue;
 
 		if (range > 1) {
-			fmt = (range == 2) ? "%s %s " : "(range %s %s) ";
-			len = snprintf(p, remaining, fmt,
-				       val_to_name[start], val_to_name[i]);
+			if (range == 2) {
+				len = snprintf(p, remaining, "%s %s ",
+					       val_to_name[start],
+					       val_to_name[i]);
+			} else {
+				len = snprintf(p, remaining, "(range %s %s) ",
+					       val_to_name[start],
+					       val_to_name[i]);
+			}
 		} else {
 			len = snprintf(p, remaining, "%s ", val_to_name[start]);
 		}
@@ -1213,7 +1230,7 @@
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
-		if (type->flavor == TYPE_ATTRIB) {
+		if (type && type->flavor == TYPE_ATTRIB) {
 			rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 			if (rc != 0) {
 				goto exit;
@@ -1343,7 +1360,7 @@
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
-		if (type->flavor == TYPE_TYPE && type->primary) {
+		if (type && type->flavor == TYPE_TYPE && type->primary) {
 			rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 			if (rc != 0) {
 				goto exit;
@@ -1472,7 +1489,7 @@
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
-		if (type->flavor == TYPE_TYPE) {
+		if (type && type->flavor == TYPE_TYPE) {
 			if (type->bounds > 0) {
 				rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 				if (rc != 0) {
@@ -1526,7 +1543,7 @@
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		attr = pdb->type_val_to_struct[i];
-		if (attr->flavor != TYPE_ATTRIB) continue;
+		if (!attr || attr->flavor != TYPE_ATTRIB) continue;
 		name = pdb->p_type_val_to_name[i];
 		typemap = &pdb->attr_type_map[i];
 		if (ebitmap_is_empty(typemap)) continue;
@@ -2259,7 +2276,7 @@
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type_datum = pdb->type_val_to_struct[i];
-		if (type_datum->flavor == TYPE_TYPE && type_datum->primary) {
+		if (type_datum && type_datum->flavor == TYPE_TYPE && type_datum->primary) {
 			rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 			if (rc != 0) {
 				goto exit;
@@ -2383,6 +2400,7 @@
 	}
 
 	for (i=0; i < pdb->p_users.nprim; i++) {
+		if (!pdb->p_user_val_to_name[i]) continue;
 		rc = strs_add(strs, pdb->p_user_val_to_name[i]);
 		if (rc != 0) {
 			goto exit;
@@ -2640,6 +2658,8 @@
 	struct ocontext *ocon;
 	struct strs *strs;
 	char *fstype, *name, *ctx;
+	uint32_t sclass;
+	const char *file_type;
 	int rc;
 
 	rc = strs_init(&strs, 32);
@@ -2652,14 +2672,43 @@
 			fstype = genfs->fstype;
 			name = ocon->u.name;
 
+			sclass = ocon->v.sclass;
+			file_type = NULL;
+			if (sclass) {
+				const char *class_name = pdb->p_class_val_to_name[sclass-1];
+				if (strcmp(class_name, "file") == 0) {
+					file_type = "file";
+				} else if (strcmp(class_name, "dir") == 0) {
+					file_type = "dir";
+				} else if (strcmp(class_name, "chr_file") == 0) {
+					file_type = "char";
+				} else if (strcmp(class_name, "blk_file") == 0) {
+					file_type = "block";
+				} else if (strcmp(class_name, "sock_file") == 0) {
+					file_type = "socket";
+				} else if (strcmp(class_name, "fifo_file") == 0) {
+					file_type = "pipe";
+				} else if (strcmp(class_name, "lnk_file") == 0) {
+					file_type = "symlink";
+				} else {
+					rc = -1;
+					goto exit;
+				}
+			}
+
 			ctx = context_to_str(pdb, &ocon->context[0]);
 			if (!ctx) {
 				rc = -1;
 				goto exit;
 			}
 
-			rc = strs_create_and_add(strs, "(genfscon %s \"%s\" %s)", 3,
-						 fstype, name, ctx);
+			if (file_type) {
+				rc = strs_create_and_add(strs, "(genfscon %s \"%s\" %s %s)", 4,
+										 fstype, name, file_type, ctx);
+			} else {
+				rc = strs_create_and_add(strs, "(genfscon %s \"%s\" %s)", 3,
+										 fstype, name, ctx);
+			}
 			free(ctx);
 			if (rc != 0) {
 				goto exit;
diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index a7453d3..972499a 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -18,6 +18,7 @@
 #include <sepol/policydb/hashtab.h>
 #include <sepol/policydb/symtab.h>
 
+#include "private.h"
 #include "kernel_to_common.h"
 
 
@@ -57,7 +58,7 @@
 	va_list vargs2;
 	char *str = NULL;
 	char *s;
-	size_t len;
+	size_t len, s_len;
 	int i, rc;
 
 	va_copy(vargs2, vargs);
@@ -66,7 +67,8 @@
 
 	for (i=0; i<num; i++) {
 		s = va_arg(vargs, char *);
-		len += strlen(s) - 2; /* -2 for each %s in fmt */
+		s_len = strlen(s);
+		len += s_len > 1 ? s_len - 2 : 0; /* -2 for each %s in fmt */
 	}
 
 	str = malloc(len);
@@ -106,6 +108,10 @@
 {
 	struct strs *new;
 
+	if (size == 0) {
+		size = 1;
+	}
+
 	*strs = NULL;
 
 	new = malloc(sizeof(struct strs));
@@ -114,7 +120,7 @@
 		return -1;
 	}
 
-	new->list = calloc(sizeof(char *), size);
+	new->list = calloc(size, sizeof(char *));
 	if (!new->list) {
 		sepol_log_err("Out of memory");
 		free(new);
@@ -159,9 +165,9 @@
 {
 	if (strs->num + 1 > strs->size) {
 		char **new;
-		unsigned i = strs->size;
+		size_t i = strs->size;
 		strs->size *= 2;
-		new = realloc(strs->list, sizeof(char *)*strs->size);
+		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
 			sepol_log_err("Out of memory");
 			return -1;
@@ -212,15 +218,15 @@
 	return strs->list[strs->num];
 }
 
-int strs_add_at_index(struct strs *strs, char *s, unsigned index)
+int strs_add_at_index(struct strs *strs, char *s, size_t index)
 {
 	if (index >= strs->size) {
 		char **new;
-		unsigned i = strs->size;
+		size_t i = strs->size;
 		while (index >= strs->size) {
 			strs->size *= 2;
 		}
-		new = realloc(strs->list, sizeof(char *)*strs->size);
+		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
 			sepol_log_err("Out of memory");
 			return -1;
@@ -237,7 +243,7 @@
 	return 0;
 }
 
-char *strs_read_at_index(struct strs *strs, unsigned index)
+char *strs_read_at_index(struct strs *strs, size_t index)
 {
 	if (index >= strs->num) {
 		return NULL;
@@ -361,6 +367,9 @@
 	int rc;
 
 	ebitmap_for_each_positive_bit(map, node, i) {
+		if (!val_to_name[i])
+			continue;
+
 		rc = strs_add(strs, val_to_name[i]);
 		if (rc != 0) {
 			return -1;
diff --git a/libsepol/src/kernel_to_common.h b/libsepol/src/kernel_to_common.h
index 8aa483f..e9932d3 100644
--- a/libsepol/src/kernel_to_common.h
+++ b/libsepol/src/kernel_to_common.h
@@ -99,8 +99,8 @@
 __attribute__ ((format(printf, 2, 4)))
 int strs_create_and_add(struct strs *strs, const char *fmt, int num, ...);
 char *strs_remove_last(struct strs *strs);
-int strs_add_at_index(struct strs *strs, char *s, unsigned index);
-char *strs_read_at_index(struct strs *strs, unsigned index);
+int strs_add_at_index(struct strs *strs, char *s, size_t index);
+char *strs_read_at_index(struct strs *strs, size_t index);
 void strs_sort(struct strs *strs);
 unsigned strs_num_items(struct strs *strs);
 size_t strs_len_items(struct strs *strs);
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index eb72e4a..3544f73 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -271,12 +271,15 @@
 {
 	struct constraint_node *curr;
 	struct strs *strs;
-	const char *format_str, *flavor;
+	const char *flavor, *perm_prefix, *perm_suffix;
 	char *perms, *expr;
 	int is_mls;
 	int rc = 0;
 
 	for (curr = constraint_rules; curr != NULL; curr = curr->next) {
+		if (curr->permissions == 0) {
+			continue;
+		}
 		expr = constraint_expr_to_str(pdb, curr->expr, &is_mls);
 		if (!expr) {
 			rc = -1;
@@ -285,9 +288,11 @@
 
 		perms = sepol_av_to_string(pdb, class->s.value, curr->permissions);
 		if (strchr(perms, ' ')) {
-			format_str = "%s %s { %s } %s;";
+			perm_prefix = "{ ";
+			perm_suffix = " }";
 		} else {
-			format_str = "%s %s %s %s";
+			perm_prefix = "";
+			perm_suffix = "";
 		}
 		if (is_mls) {
 			flavor = "mlsconstrain";
@@ -297,8 +302,10 @@
 			strs = non_mls_list;
 		}
 
-		rc = strs_create_and_add(strs, format_str, 4,
-					 flavor, classkey, perms+1, expr);
+		rc = strs_create_and_add(strs, "%s %s %s%s%s %s;", 6,
+					 flavor, classkey,
+					 perm_prefix, perms+1, perm_suffix,
+					 expr);
 		free(expr);
 		if (rc != 0) {
 			goto exit;
@@ -358,7 +365,7 @@
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
-		if (class->constraints) {
+		if (class && class->constraints) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs);
 			if (rc != 0) {
@@ -383,7 +390,7 @@
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
-		if (class->validatetrans) {
+		if (class && class->validatetrans) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs);
 			if (rc != 0) {
@@ -551,6 +558,7 @@
 	}
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = class->comkey;
 		if (!name) continue;
 		common = hashtab_search(pdb->p_commons.table, name);
@@ -577,6 +585,7 @@
 	/* class */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		sepol_printf(out, "class %s", name);
 		if (class->comkey) {
@@ -702,6 +711,7 @@
 	/* default_user */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_user != 0) {
 			rc = write_default_user_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -713,6 +723,7 @@
 	/* default_role */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_role != 0) {
 			rc = write_default_role_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -724,6 +735,7 @@
 	/* default_type */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_type != 0) {
 			rc = write_default_type_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -739,6 +751,7 @@
 	/* default_range */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_range != 0) {
 			rc = write_default_range_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -908,7 +921,7 @@
 	unsigned i, j, num;
 	int rc = 0;
 
-	rc = strs_init(&strs, pdb->p_levels.nprim);
+	rc = strs_init(&strs, pdb->p_cats.nprim);
 	if (rc != 0) {
 		goto exit;
 	}
@@ -1026,7 +1039,6 @@
 	struct ebitmap_node *node;
 	uint32_t i, start, range, first;
 	char *catsbuf = NULL, *p;
-	const char *fmt;
 	char sep;
 	int len, remaining;
 
@@ -1054,12 +1066,12 @@
 
 		if (range > 1) {
 			sep = (range == 2) ? ',' : '.';
-			fmt = first ? "%s%c%s" : ",%s%c%s";
-			len = snprintf(p, remaining, fmt,
+			len = snprintf(p, remaining, "%s%s%c%s",
+				       first ? "" : ",",
 				       val_to_name[start], sep, val_to_name[i]);
 		} else {
-			fmt = first ? "%s" : ",%s";
-			len = snprintf(p, remaining, fmt, val_to_name[start]);
+			len = snprintf(p, remaining, "%s%s", first ? "" : ",",
+				       val_to_name[start]);
 
 		}
 		if (len < 0 || len >= remaining) {
@@ -1201,7 +1213,7 @@
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
-		if (type->flavor == TYPE_ATTRIB) {
+		if (type && type->flavor == TYPE_ATTRIB) {
 			rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 			if (rc != 0) {
 				goto exit;
@@ -1331,7 +1343,7 @@
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
-		if (type->flavor == TYPE_TYPE && type->primary) {
+		if (type && type->flavor == TYPE_TYPE && type->primary) {
 			rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 			if (rc != 0) {
 				goto exit;
@@ -1451,7 +1463,7 @@
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
-		if (type->flavor == TYPE_TYPE) {
+		if (type && type->flavor == TYPE_TYPE) {
 			if (type->bounds > 0) {
 				rc = strs_add(strs, pdb->p_type_val_to_name[i]);
 				if (rc != 0) {
@@ -1574,7 +1586,7 @@
 
 	for (i=0; i < pdb->p_types.nprim; i++) {
 		type = pdb->type_val_to_struct[i];
-		if (type->flavor != TYPE_TYPE || !type->primary) continue;
+		if (!type || type->flavor != TYPE_TYPE || !type->primary) continue;
 		if (ebitmap_cardinality(&pdb->type_attr_map[i]) == 1) continue;
 
 		rc = ebitmap_cpy(&attrmap, &pdb->type_attr_map[i]);
@@ -2318,6 +2330,7 @@
 	}
 
 	for (i=0; i < pdb->p_users.nprim; i++) {
+		if (!pdb->p_user_val_to_name[i]) continue;
 		rc = strs_add(strs, pdb->p_user_val_to_name[i]);
 		if (rc != 0) {
 			goto exit;
@@ -2513,6 +2526,8 @@
 	struct ocontext *ocon;
 	struct strs *strs;
 	char *fstype, *name, *ctx;
+	uint32_t sclass;
+	const char *file_type;
 	int rc;
 
 	rc = strs_init(&strs, 32);
@@ -2525,14 +2540,43 @@
 			fstype = genfs->fstype;
 			name = ocon->u.name;
 
+			sclass = ocon->v.sclass;
+			file_type = NULL;
+			if (sclass) {
+				const char *class_name = pdb->p_class_val_to_name[sclass-1];
+				if (strcmp(class_name, "file") == 0) {
+					file_type = "--";
+				} else if (strcmp(class_name, "dir") == 0) {
+					file_type = "-d";
+				} else if (strcmp(class_name, "chr_file") == 0) {
+					file_type = "-c";
+				} else if (strcmp(class_name, "blk_file") == 0) {
+					file_type = "-b";
+				} else if (strcmp(class_name, "sock_file") == 0) {
+					file_type = "-s";
+				} else if (strcmp(class_name, "fifo_file") == 0) {
+					file_type = "-p";
+				} else if (strcmp(class_name, "lnk_file") == 0) {
+					file_type = "-l";
+				} else {
+					rc = -1;
+					goto exit;
+				}
+			}
+
 			ctx = context_to_str(pdb, &ocon->context[0]);
 			if (!ctx) {
 				rc = -1;
 				goto exit;
 			}
 
-			rc = strs_create_and_add(strs, "genfscon %s \"%s\" %s", 3,
-						 fstype, name, ctx);
+			if (file_type) {
+				rc = strs_create_and_add(strs, "genfscon %s \"%s\" %s %s", 4,
+										 fstype, name, file_type, ctx);
+			} else {
+				rc = strs_create_and_add(strs, "genfscon %s \"%s\" %s", 3,
+										 fstype, name, ctx);
+			}
 			free(ctx);
 			if (rc != 0) {
 				goto exit;
diff --git a/libsepol/src/link.c b/libsepol/src/link.c
index 7512a4d..21a5a93 100644
--- a/libsepol/src/link.c
+++ b/libsepol/src/link.c
@@ -34,6 +34,7 @@
 #include <assert.h>
 
 #include "debug.h"
+#include "private.h"
 
 #undef min
 #define min(a,b) (((a) < (b)) ? (a) : (b))
@@ -164,7 +165,7 @@
 					     (hashtab_datum_t) new_perm);
 			if (ret) {
 				ERR(state->handle,
-				    "could not insert permission into class\n");
+				    "could not insert permission into class");
 				goto err;
 			}
 			new_perm->s.value = dest_class->permissions.nprim + 1;
@@ -190,8 +191,9 @@
 			ERR(state->handle, "Out of memory!");
 			return -1;
 		}
-		memcpy(newmap, mod->perm_map[sclassi],
-		       mod->perm_map_len[sclassi] * sizeof(*newmap));
+		if (mod->perm_map_len[sclassi] > 0) {
+			memcpy(newmap, mod->perm_map[sclassi], mod->perm_map_len[sclassi] * sizeof(*newmap));
+		}
 		free(mod->perm_map[sclassi]);
 		mod->perm_map[sclassi] = newmap;
 		mod->perm_map_len[sclassi] = perm->s.value;
@@ -287,7 +289,7 @@
 			new_class =
 			    (class_datum_t *) calloc(1, sizeof(class_datum_t));
 			if (new_class == NULL) {
-				ERR(state->handle, "Memory error\n");
+				ERR(state->handle, "Memory error");
 				ret = SEPOL_ERR;
 				goto err;
 			}
@@ -298,7 +300,7 @@
 			}
 			new_id = strdup(id);
 			if (new_id == NULL) {
-				ERR(state->handle, "Memory error\n");
+				ERR(state->handle, "Memory error");
 				symtab_destroy(&new_class->permissions);
 				ret = SEPOL_ERR;
 				goto err;
@@ -694,7 +696,7 @@
 			return SEPOL_ENOTSUP;
 		} else {
 			ERR(state->handle,
-			    "%s: has an unknown scope: %d\n",
+			    "%s: has an unknown scope: %d",
 			    state->cur_mod_name, scope->scope);
 			return SEPOL_ENOTSUP;
 		}
@@ -736,7 +738,7 @@
 		} else {
 			/* unknown scope?  malformed policy? */
 			ERR(state->handle,
-			    "%s: has an unknown scope: %d\n",
+			    "%s: has an unknown scope: %d",
 			    state->cur_mod_name, scope->scope);
 			return SEPOL_ENOTSUP;
 		}
@@ -1679,7 +1681,7 @@
 	}
 
 	/* next copy the enabled permissions data  */
-	if ((dest->class_perms_map = malloc(largest_mapped_class_value *
+	if ((dest->class_perms_map = mallocarray(largest_mapped_class_value,
 					    sizeof(*dest->class_perms_map))) ==
 	    NULL) {
 		goto cleanup;
@@ -1779,7 +1781,7 @@
 		if (module->policy->name != NULL) {
 			new_decl->module_name = strdup(module->policy->name);
 			if (new_decl->module_name == NULL) {
-				ERR(state->handle, "Out of memory\n");
+				ERR(state->handle, "Out of memory");
 				avrule_decl_destroy(new_decl);
 				ret = -1;
 				goto cleanup;
@@ -2206,7 +2208,7 @@
 			if (state->verbose) {
 				const char *mod_name = decl->module_name ?
 				    decl->module_name : "BASE";
-				INFO(state->handle, "check module %s decl %d\n",
+				INFO(state->handle, "check module %s decl %d",
 				     mod_name, decl->decl_id);
 			}
 			rc = is_decl_requires_met(state, decl, &req);
@@ -2552,7 +2554,7 @@
 
 		if (mods[i]->policyvers > b->policyvers) {
 			WARN(state.handle,
-			     "Upgrading policy version from %u to %u\n", b->policyvers, mods[i]->policyvers);
+			     "Upgrading policy version from %u to %u", b->policyvers, mods[i]->policyvers);
 			b->policyvers = mods[i]->policyvers;
 		}
 
diff --git a/libsepol/src/module.c b/libsepol/src/module.c
index 02a5de2..d93d08a 100644
--- a/libsepol/src/module.c
+++ b/libsepol/src/module.c
@@ -293,11 +293,14 @@
 	}
 	base->netfilter_contexts = base_context;
 	for (i = 0; i < num_modules; i++) {
-		memcpy(base->netfilter_contexts + base->netfilter_contexts_len,
-		       modules[i]->netfilter_contexts,
-		       modules[i]->netfilter_contexts_len);
-		base->netfilter_contexts_len +=
-		    modules[i]->netfilter_contexts_len;
+		if (modules[i]->netfilter_contexts_len > 0) {
+			memcpy(base->netfilter_contexts + base->netfilter_contexts_len,
+			       modules[i]->netfilter_contexts,
+			       modules[i]->netfilter_contexts_len);
+			base->netfilter_contexts_len +=
+			    modules[i]->netfilter_contexts_len;
+		}
+
 	}
 	return 0;
 }
@@ -406,14 +409,14 @@
 		goto err;
 	}
 
-	off = (size_t *) malloc((nsec + 1) * sizeof(size_t));
+	off = (size_t *) mallocarray(nsec + 1, sizeof(size_t));
 	if (!off) {
 		ERR(file->handle, "out of memory");
 		goto err;
 	}
 
 	free(buf);
-	buf = malloc(sizeof(uint32_t) * nsec);
+	buf = mallocarray(nsec, sizeof(uint32_t));
 	if (!buf) {
 		ERR(file->handle, "out of memory");
 		goto err;
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index 16e4004..c9e88f1 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -430,7 +430,7 @@
 		goto exit;
 	}
 
-	s->stack = malloc(sizeof(*s->stack) * STACK_SIZE);
+	s->stack = mallocarray(STACK_SIZE, sizeof(*s->stack));
 	if (s->stack == NULL) {
 		goto exit;
 	}
@@ -453,7 +453,7 @@
 	void *new_stack;
 
 	if (stack->pos + 1 == stack->size) {
-		new_stack = realloc(stack->stack, sizeof(*stack->stack) * (stack->size * 2));
+		new_stack = reallocarray(stack->stack, stack->size * 2, sizeof(*stack->stack));
 		if (new_stack == NULL) {
 			goto exit;
 		}
@@ -1008,7 +1008,7 @@
 		goto exit;
 	}
 
-	name_arr = malloc(sizeof(*name_arr) * num);
+	name_arr = mallocarray(num, sizeof(*name_arr));
 	if (name_arr == NULL) {
 		log_err("Out of memory");
 		rc = -1;
@@ -1259,7 +1259,7 @@
 	char *val2 = NULL;
 	unsigned int num_params;
 	const char *op;
-	const char *fmt_str;
+	const char *sep;
 	const char *type;
 
 	rc = stack_init(&stack);
@@ -1308,11 +1308,11 @@
 					rc = -1;
 					goto exit;
 				}
-				fmt_str = "(%s %s)";
+				sep = "";
 			} else {
 				val2 = stack_pop(stack);
 				val1 = stack_pop(stack);
-				fmt_str = "(%s %s %s)";
+				sep = " ";
 			}
 
 			if (val1 == NULL || val2 == NULL) {
@@ -1334,10 +1334,7 @@
 				goto exit;
 			}
 
-			// although we always supply val2 and there isn't always a 2nd
-			// value, it should only be used when there are actually two values
-			// in the format strings
-			rlen = snprintf(new_val, len, fmt_str, op, val1, val2);
+			rlen = snprintf(new_val, len, "(%s %s%s%s)", op, val1, sep, val2);
 			if (rlen < 0 || rlen >= len) {
 				log_err("Failed to generate conditional expression");
 				rc = -1;
@@ -1711,7 +1708,7 @@
 	char *val2 = NULL;
 	uint32_t num_params;
 	const char *op;
-	const char *fmt_str;
+	const char *sep;
 	const char *attr1;
 	const char *attr2;
 	char *names = NULL;
@@ -1849,11 +1846,11 @@
 					rc = -1;
 					goto exit;
 				}
-				fmt_str = "(%s %s)";
+				sep = "";
 			} else {
 				val2 = stack_pop(stack);
 				val1 = stack_pop(stack);
-				fmt_str = "(%s %s %s)";
+				sep = " ";
 			}
 
 			if (val1 == NULL || val2 == NULL) {
@@ -1875,10 +1872,7 @@
 				goto exit;
 			}
 
-			// although we always supply val2 and there isn't always a 2nd
-			// value, it should only be used when there are actually two values
-			// in the format strings
-			rlen = snprintf(new_val, len, fmt_str, op, val1, val2);
+			rlen = snprintf(new_val, len, "(%s %s%s%s)", op, val1, sep, val2);
 			if (rlen < 0 || rlen >= len) {
 				log_err("Failed to generate constraint expression");
 				rc = -1;
@@ -2961,10 +2955,35 @@
 {
 	struct genfs *genfs;
 	struct ocontext *ocon;
+	uint32_t sclass;
 
 	for (genfs = pdb->genfs; genfs != NULL; genfs = genfs->next) {
 		for (ocon = genfs->head; ocon != NULL; ocon = ocon->next) {
-			cil_printf("(genfscon %s \"%s\" ", genfs->fstype, ocon->u.name);
+			sclass = ocon->v.sclass;
+			if (sclass) {
+				const char *file_type;
+				const char *class_name = pdb->p_class_val_to_name[sclass-1];
+				if (strcmp(class_name, "file") == 0) {
+					file_type = "file";
+				} else if (strcmp(class_name, "dir") == 0) {
+					file_type = "dir";
+				} else if (strcmp(class_name, "chr_file") == 0) {
+					file_type = "char";
+				} else if (strcmp(class_name, "blk_file") == 0) {
+					file_type = "block";
+				} else if (strcmp(class_name, "sock_file") == 0) {
+					file_type = "socket";
+				} else if (strcmp(class_name, "fifo_file") == 0) {
+					file_type = "pipe";
+				} else if (strcmp(class_name, "lnk_file") == 0) {
+					file_type = "symlink";
+				} else {
+					return -1;
+				}
+				cil_printf("(genfscon %s \"%s\" %s ", genfs->fstype, ocon->u.name, file_type);
+			} else {
+				cil_printf("(genfscon %s \"%s\" ", genfs->fstype, ocon->u.name);
+			}
 			context_to_cil(pdb, &ocon->context[0]);
 			cil_printf(")\n");
 		}
@@ -4123,7 +4142,7 @@
 static int fp_to_buffer(FILE *fp, char **data, size_t *data_len)
 {
 	int rc = -1;
-	char *d = NULL;
+	char *d = NULL, *d_tmp;
 	size_t d_len = 0;
 	size_t read_len = 0;
 	size_t max_len = 1 << 17; // start at 128KB, this is enough to hold about half of all the existing pp files
@@ -4139,12 +4158,13 @@
 		d_len += read_len;
 		if (d_len == max_len) {
 			max_len *= 2;
-			d = realloc(d, max_len);
-			if (d == NULL) {
+			d_tmp = realloc(d, max_len);
+			if (d_tmp == NULL) {
 				log_err("Out of memory");
 				rc = -1;
 				goto exit;
 			}
+			d = d_tmp;
 		}
 	}
 
diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
index 6826155..93ff211 100644
--- a/libsepol/src/optimize.c
+++ b/libsepol/src/optimize.c
@@ -31,6 +31,9 @@
 #include <sepol/policydb/policydb.h>
 #include <sepol/policydb/conditional.h>
 
+#include "debug.h"
+#include "private.h"
+
 #define TYPE_VEC_INIT_SIZE 16
 
 struct type_vec {
@@ -42,7 +45,7 @@
 {
 	v->capacity = TYPE_VEC_INIT_SIZE;
 	v->count = 0;
-	v->types = malloc(v->capacity * sizeof(*v->types));
+	v->types = mallocarray(v->capacity, sizeof(*v->types));
 	if (!v->types)
 		return -1;
 	return 0;
@@ -57,8 +60,9 @@
 {
 	if (v->capacity == v->count) {
 		unsigned int new_capacity = v->capacity * 2;
-		uint32_t *new_types = realloc(v->types,
-					      new_capacity * sizeof(*v->types));
+		uint32_t *new_types = reallocarray(v->types,
+						   new_capacity,
+						   sizeof(*v->types));
 		if (!new_types)
 			return -1;
 
@@ -93,7 +97,7 @@
 {
 	unsigned int i, k;
 	ebitmap_node_t *n;
-	struct type_vec *map = malloc(p->p_types.nprim * sizeof(*map));
+	struct type_vec *map = mallocarray(p->p_types.nprim, sizeof(*map));
 	if (!map)
 		return NULL;
 
@@ -101,6 +105,9 @@
 		if (type_vec_init(&map[i]))
 			goto err;
 
+		if (!p->type_val_to_struct[i])
+			continue;
+
 		if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) {
 			ebitmap_for_each_positive_bit(&p->type_attr_map[i],
 						      n, k) {
@@ -111,11 +118,13 @@
 			ebitmap_t *types_i = &p->attr_type_map[i];
 
 			for (k = 0; k < p->p_types.nprim; k++) {
-				ebitmap_t *types_k = &p->attr_type_map[k];
+				const ebitmap_t *types_k;
 
-				if (p->type_val_to_struct[k]->flavor != TYPE_ATTRIB)
+				if (!p->type_val_to_struct[k] || p->type_val_to_struct[k]->flavor != TYPE_ATTRIB)
 					continue;
 
+				types_k = &p->attr_type_map[k];
+
 				if (ebitmap_contains(types_k, types_i)) {
 					if (type_vec_append(&map[i], k))
 						goto err;
@@ -435,6 +444,17 @@
 	if (p->policy_type != POLICY_KERN)
 		return -1;
 
+	if (p->policyvers >= POLICYDB_VERSION_AVTAB && p->policyvers <= POLICYDB_VERSION_PERMISSIVE) {
+		/*
+		 * For policy versions between 20 and 23, attributes exist in the policy,
+		 * but only in the type_attr_map. This means that there are gaps in both
+		 * the type_val_to_struct and p_type_val_to_name arrays and policy rules
+		 * can refer to those gaps.
+		 */
+		ERR(NULL, "Optimizing policy versions between 20 and 23 is not supported");
+		return -1;
+	}
+
 	type_map = build_type_map(p);
 	if (!type_map)
 		return -1;
diff --git a/libsepol/src/polcaps.c b/libsepol/src/polcaps.c
index 6a74ec7..687e971 100644
--- a/libsepol/src/polcaps.c
+++ b/libsepol/src/polcaps.c
@@ -6,13 +6,14 @@
 #include <sepol/policydb/polcaps.h>
 
 static const char * const polcap_names[] = {
-	"network_peer_controls",	/* POLICYDB_CAPABILITY_NETPEER */
-	"open_perms",			/* POLICYDB_CAPABILITY_OPENPERM */
-	"extended_socket_class",	/* POLICYDB_CAPABILITY_EXTSOCKCLASS */
-	"always_check_network",		/* POLICYDB_CAPABILITY_ALWAYSNETWORK */
-	"cgroup_seclabel",		/* POLICYDB_CAPABILITY_SECLABEL */
-	"nnp_nosuid_transition",	/* POLICYDB_CAPABILITY_NNP_NOSUID_TRANSITION */
-	"genfs_seclabel_symlinks",	/* POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS */
+	"network_peer_controls",	/* POLICYDB_CAP_NETPEER */
+	"open_perms",			/* POLICYDB_CAP_OPENPERM */
+	"extended_socket_class",	/* POLICYDB_CAP_EXTSOCKCLASS */
+	"always_check_network",		/* POLICYDB_CAP_ALWAYSNETWORK */
+	"cgroup_seclabel",		/* POLICYDB_CAP_SECLABEL */
+	"nnp_nosuid_transition",	/* POLICYDB_CAP_NNP_NOSUID_TRANSITION */
+	"genfs_seclabel_symlinks",	/* POLICYDB_CAP_GENFS_SECLABEL_SYMLINKS */
+	"ioctl_skip_cloexec",		/* POLICYDB_CAP_IOCTL_SKIP_CLOEXEC */
 	NULL
 };
 
@@ -20,7 +21,7 @@
 {
 	int capnum;
 
-	for (capnum = 0; capnum <= POLICYDB_CAPABILITY_MAX; capnum++) {
+	for (capnum = 0; capnum <= POLICYDB_CAP_MAX; capnum++) {
 		if (polcap_names[capnum] == NULL)
 			continue;
 		if (strcasecmp(polcap_names[capnum], name) == 0)
@@ -31,7 +32,7 @@
 
 const char *sepol_polcap_getname(unsigned int capnum)
 {
-	if (capnum > POLICYDB_CAPABILITY_MAX)
+	if (capnum > POLICYDB_CAP_MAX)
 		return NULL;
 
 	return polcap_names[capnum];
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 587ba64..fc71463 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2103,6 +2103,8 @@
 	if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE))
 		goto bad;
 	comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+	if (comdatum->permissions.nprim > PERM_SYMTAB_SIZE)
+		goto bad;
 	nel = le32_to_cpu(buf[3]);
 
 	key = malloc(len + 1);
@@ -2251,6 +2253,8 @@
 	if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE))
 		goto bad;
 	cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+	if (cladatum->permissions.nprim > PERM_SYMTAB_SIZE)
+		goto bad;
 	nel = le32_to_cpu(buf[4]);
 
 	ncons = le32_to_cpu(buf[5]);
@@ -2679,7 +2683,10 @@
 	if (rc < 0)
 		goto err;
 
-	stype  = le32_to_cpu(buf[0]);
+	stype = le32_to_cpu(buf[0]);
+	if (stype == 0)
+		goto err;
+
 	ttype  = le32_to_cpu(buf[1]);
 	tclass = le32_to_cpu(buf[2]);
 	otype  = le32_to_cpu(buf[3]);
@@ -2773,6 +2780,7 @@
 		if (!datum)
 			goto err;
 
+		datum->next = NULL;
 		*dst = datum;
 
 		/* ebitmap_read() will at least init the bitmap */
@@ -2790,7 +2798,6 @@
 
 		dst = &datum->next;
 	}
-	*dst = NULL;
 
 	if (ndatum > 1 && filename_trans_check_datum(first))
 		goto err;
@@ -2879,6 +2886,8 @@
 				if (rc < 0)
 					return -1;
 				c->sid[0] = le32_to_cpu(buf[0]);
+				if (is_saturated(c->sid[0]))
+					return -1;
 				if (context_read_and_validate
 				    (&c->context[0], p, fp))
 					return -1;
@@ -2990,6 +2999,8 @@
 				if (rc < 0)
 					return -1;
 				c->sid[0] = le32_to_cpu(buf[0]);
+				if (is_saturated(c->sid[0]))
+					return -1;
 				if (context_read_and_validate
 				    (&c->context[0], p, fp))
 					return -1;
@@ -3926,6 +3937,8 @@
 	if (rc < 0)
 		return -1;
 	scope_index->class_perms_len = le32_to_cpu(buf[0]);
+	if (is_saturated(scope_index->class_perms_len))
+		return -1;
 	if (scope_index->class_perms_len == 0) {
 		scope_index->class_perms_map = NULL;
 		return 0;
@@ -3980,6 +3993,8 @@
 		if (rc < 0) 
 			return -1;
 		nprim = le32_to_cpu(buf[0]);
+		if (is_saturated(nprim))
+			return -1;
 		nel = le32_to_cpu(buf[1]);
 		for (j = 0; j < nel; j++) {
 			if (read_f[i] (p, decl->symtab[i].table, fp)) {
@@ -4106,12 +4121,12 @@
 		goto cleanup;
 	scope->scope = le32_to_cpu(buf[0]);
 	scope->decl_ids_len = le32_to_cpu(buf[1]);
-	if (scope->decl_ids_len == 0) {
+	if (zero_or_saturated(scope->decl_ids_len)) {
 		ERR(fp->handle, "invalid scope with no declaration");
 		goto cleanup;
 	}
 	if ((scope->decl_ids =
-	     malloc(scope->decl_ids_len * sizeof(uint32_t))) == NULL) {
+	     mallocarray(scope->decl_ids_len, sizeof(uint32_t))) == NULL) {
 		goto cleanup;
 	}
 	rc = next_entry(scope->decl_ids, fp, sizeof(uint32_t) * scope->decl_ids_len);
@@ -4396,6 +4411,8 @@
 		if (rc < 0)
 			goto bad;
 		nprim = le32_to_cpu(buf[0]);
+		if (is_saturated(nprim))
+			goto bad;
 		nel = le32_to_cpu(buf[1]);
 		if (nel && !nprim) {
 			ERR(fp->handle, "unexpected items in symbol table with no symbol");
@@ -4500,8 +4517,8 @@
 	}
 
 	if (policy_type == POLICY_KERN) {
-		p->type_attr_map = malloc(p->p_types.nprim * sizeof(ebitmap_t));
-		p->attr_type_map = malloc(p->p_types.nprim * sizeof(ebitmap_t));
+		p->type_attr_map = mallocarray(p->p_types.nprim, sizeof(ebitmap_t));
+		p->attr_type_map = mallocarray(p->p_types.nprim, sizeof(ebitmap_t));
 		if (!p->type_attr_map || !p->attr_type_map)
 			goto bad;
 		for (i = 0; i < p->p_types.nprim; i++) {
diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 5804d24..a2dcebe 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -2,15 +2,24 @@
 #include <sepol/policydb/conditional.h>
 #include <sepol/policydb/ebitmap.h>
 #include <sepol/policydb/policydb.h>
+#include <sepol/policydb/services.h>
 
 #include "debug.h"
 #include "policydb_validate.h"
 
+#define bool_xor(a, b) (!(a) != !(b))
+#define bool_xnor(a, b) !bool_xor(a, b)
+
 typedef struct validate {
 	uint32_t nprim;
 	ebitmap_t gaps;
 } validate_t;
 
+typedef struct map_arg {
+	validate_t *flavors;
+	sepol_handle_t *handle;
+	int mls;
+} map_arg_t;
 
 static int create_gap_ebitmap(char **val_to_name, uint32_t nprim, ebitmap_t *gaps)
 {
@@ -115,6 +124,30 @@
 	if (validate_ebitmap(&type_set->negset, type))
 		goto bad;
 
+	switch (type_set->flags) {
+	case 0:
+	case TYPE_STAR:
+	case TYPE_COMP:
+		break;
+	default:
+		goto bad;
+	}
+
+	return 0;
+
+bad:
+	return -1;
+}
+
+static int validate_empty_type_set(type_set_t *type_set)
+{
+	if (!ebitmap_is_empty(&type_set->types))
+		goto bad;
+	if (!ebitmap_is_empty(&type_set->negset))
+		goto bad;
+	if (type_set->flags != 0)
+		goto bad;
+
 	return 0;
 
 bad:
@@ -124,9 +157,21 @@
 static int validate_role_set(role_set_t *role_set, validate_t *role)
 {
 	if (validate_ebitmap(&role_set->roles, role))
-		return -1;
+		goto bad;
+
+	switch (role_set->flags) {
+	case 0:
+	case ROLE_STAR:
+	case ROLE_COMP:
+		break;
+	default:
+		goto bad;
+	}
 
 	return 0;
+
+bad:
+	return -1;
 }
 
 static int validate_scope(__attribute__ ((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
@@ -135,12 +180,23 @@
 	uint32_t *nprim = (uint32_t *)args;
 	unsigned int i;
 
+	switch (scope_datum->scope) {
+	case SCOPE_REQ:
+	case SCOPE_DECL:
+		break;
+	default:
+		goto bad;
+	}
+
 	for (i = 0; i < scope_datum->decl_ids_len; i++) {
 		if (!value_isvalid(scope_datum->decl_ids[i], *nprim))
-			return -1;
+			goto bad;
 	}
 
 	return 0;
+
+bad:
+	return -1;
 }
 
 static int validate_scopes(sepol_handle_t *handle, symtab_t scopes[], avrule_block_t *block)
@@ -167,22 +223,110 @@
 	return -1;
 }
 
-static int validate_constraint_nodes(sepol_handle_t *handle, constraint_node_t *cons, validate_t flavors[])
+static int validate_constraint_nodes(sepol_handle_t *handle, unsigned int nperms, constraint_node_t *cons, validate_t flavors[])
 {
 	constraint_expr_t *cexp;
 
 	for (; cons; cons = cons->next) {
+		if (nperms == 0 && cons->permissions != 0)
+			goto bad;
+		if (nperms > 0 && cons->permissions == 0)
+			goto bad;
+		if (nperms > 0 && nperms != PERM_SYMTAB_SIZE && cons->permissions >= (UINT32_C(1) << nperms))
+			goto bad;
+
 		for (cexp = cons->expr; cexp; cexp = cexp->next) {
-			if (cexp->attr & CEXPR_USER) {
-				if (validate_ebitmap(&cexp->names, &flavors[SYM_USERS]))
+			if (cexp->expr_type == CEXPR_NAMES) {
+				if (cexp->attr & CEXPR_XTARGET && nperms != 0)
 					goto bad;
-			} else if (cexp->attr & CEXPR_ROLE) {
-				if (validate_ebitmap(&cexp->names, &flavors[SYM_ROLES]))
+				if (!(cexp->attr & CEXPR_TYPE)) {
+					if (validate_empty_type_set(cexp->type_names))
+						goto bad;
+				}
+
+				switch (cexp->op) {
+				case CEXPR_EQ:
+				case CEXPR_NEQ:
+					break;
+				default:
 					goto bad;
-			} else if (cexp->attr & CEXPR_TYPE) {
-				if (validate_ebitmap(&cexp->names, &flavors[SYM_TYPES]))
+				}
+
+				switch (cexp->attr) {
+				case CEXPR_USER:
+				case CEXPR_USER | CEXPR_TARGET:
+				case CEXPR_USER | CEXPR_XTARGET:
+					if (validate_ebitmap(&cexp->names, &flavors[SYM_USERS]))
+						goto bad;
+					break;
+				case CEXPR_ROLE:
+				case CEXPR_ROLE | CEXPR_TARGET:
+				case CEXPR_ROLE | CEXPR_XTARGET:
+					if (validate_ebitmap(&cexp->names, &flavors[SYM_ROLES]))
+						goto bad;
+					break;
+				case CEXPR_TYPE:
+				case CEXPR_TYPE | CEXPR_TARGET:
+				case CEXPR_TYPE | CEXPR_XTARGET:
+					if (validate_ebitmap(&cexp->names, &flavors[SYM_TYPES]))
+						goto bad;
+					if (validate_type_set(cexp->type_names, &flavors[SYM_TYPES]))
+						goto bad;
+					break;
+				default:
 					goto bad;
-				if (validate_type_set(cexp->type_names, &flavors[SYM_TYPES]))
+				}
+			} else if (cexp->expr_type == CEXPR_ATTR) {
+				if (!ebitmap_is_empty(&cexp->names))
+					goto bad;
+				if (validate_empty_type_set(cexp->type_names))
+					goto bad;
+
+				switch (cexp->op) {
+				case CEXPR_EQ:
+				case CEXPR_NEQ:
+					break;
+				case CEXPR_DOM:
+				case CEXPR_DOMBY:
+				case CEXPR_INCOMP:
+					if ((cexp->attr & CEXPR_USER) || (cexp->attr & CEXPR_TYPE))
+						goto bad;
+					break;
+				default:
+					goto bad;
+				}
+
+				switch (cexp->attr) {
+				case CEXPR_USER:
+				case CEXPR_ROLE:
+				case CEXPR_TYPE:
+				case CEXPR_L1L2:
+				case CEXPR_L1H2:
+				case CEXPR_H1L2:
+				case CEXPR_H1H2:
+				case CEXPR_L1H1:
+				case CEXPR_L2H2:
+					break;
+				default:
+					goto bad;
+				}
+			} else {
+				switch (cexp->expr_type) {
+				case CEXPR_NOT:
+				case CEXPR_AND:
+				case CEXPR_OR:
+					break;
+				default:
+					goto bad;
+				}
+
+				if (cexp->op != 0)
+					goto bad;
+				if (cexp->attr != 0)
+					goto bad;
+				if (!ebitmap_is_empty(&cexp->names))
+					goto bad;
+				if (validate_empty_type_set(cexp->type_names))
 					goto bad;
 			}
 		}
@@ -199,10 +343,53 @@
 {
 	if (validate_value(class->s.value, &flavors[SYM_CLASSES]))
 		goto bad;
-	if (validate_constraint_nodes(handle, class->constraints, flavors))
+	if (class->permissions.nprim > PERM_SYMTAB_SIZE)
 		goto bad;
-	if (validate_constraint_nodes(handle, class->validatetrans, flavors))
+	if (validate_constraint_nodes(handle, class->permissions.nprim, class->constraints, flavors))
 		goto bad;
+	if (validate_constraint_nodes(handle, 0, class->validatetrans, flavors))
+		goto bad;
+
+	switch (class->default_user) {
+	case 0:
+	case DEFAULT_SOURCE:
+	case DEFAULT_TARGET:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (class->default_role) {
+	case 0:
+	case DEFAULT_SOURCE:
+	case DEFAULT_TARGET:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (class->default_type) {
+	case 0:
+	case DEFAULT_SOURCE:
+	case DEFAULT_TARGET:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (class->default_range) {
+	case 0:
+	case DEFAULT_SOURCE_LOW:
+	case DEFAULT_SOURCE_HIGH:
+	case DEFAULT_SOURCE_LOW_HIGH:
+	case DEFAULT_TARGET_LOW:
+	case DEFAULT_TARGET_HIGH:
+	case DEFAULT_TARGET_LOW_HIGH:
+	case DEFAULT_GLBLUB:
+		break;
+	default:
+		goto bad;
+	}
 
 	return 0;
 
@@ -211,6 +398,32 @@
 	return -1;
 }
 
+static int validate_class_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_class_datum(margs->handle, d, margs->flavors);
+}
+
+static int validate_common_datum(sepol_handle_t *handle, common_datum_t *common)
+{
+	if (common->permissions.nprim > PERM_SYMTAB_SIZE)
+		goto bad;
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid common class datum");
+	return -1;
+}
+
+static int validate_common_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_common_datum(margs->handle, d);
+}
+
 static int validate_role_datum(sepol_handle_t *handle, role_datum_t *role, validate_t flavors[])
 {
 	if (validate_value(role->s.value, &flavors[SYM_ROLES]))
@@ -227,10 +440,17 @@
 	return 0;
 
 bad:
-	ERR(handle, "Invalid class datum");
+	ERR(handle, "Invalid role datum");
 	return -1;
 }
 
+static int validate_role_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_role_datum(margs->handle, d, margs->flavors);
+}
+
 static int validate_type_datum(sepol_handle_t *handle, type_datum_t *type, validate_t flavors[])
 {
 	if (validate_value(type->s.value, &flavors[SYM_TYPES]))
@@ -240,6 +460,26 @@
 	if (type->bounds && validate_value(type->bounds, &flavors[SYM_TYPES]))
 		goto bad;
 
+	switch (type->flavor) {
+	case TYPE_TYPE:
+	case TYPE_ATTRIB:
+	case TYPE_ALIAS:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (type->flags) {
+	case 0:
+	case TYPE_FLAGS_PERMISSIVE:
+	case TYPE_FLAGS_EXPAND_ATTR_TRUE:
+	case TYPE_FLAGS_EXPAND_ATTR_FALSE:
+	case TYPE_FLAGS_EXPAND_ATTR:
+		break;
+	default:
+		goto bad;
+	}
+
 	return 0;
 
 bad:
@@ -247,6 +487,13 @@
 	return -1;
 }
 
+static int validate_type_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_type_datum(margs->handle, d, margs->flavors);
+}
+
 static int validate_mls_semantic_cat(mls_semantic_cat_t *cat, validate_t *cats)
 {
 	for (; cat; cat = cat->next) {
@@ -290,7 +537,41 @@
 	return -1;
 }
 
-static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[])
+static int validate_mls_level(mls_level_t *level, validate_t *sens, validate_t *cats)
+{
+	if (validate_value(level->sens, sens))
+		goto bad;
+	if (validate_ebitmap(&level->cat, cats))
+		goto bad;
+
+	return 0;
+
+	bad:
+	return -1;
+}
+
+static int validate_level_datum(__attribute__ ((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	level_datum_t *level = d;
+	validate_t *flavors = args;
+
+	return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]);
+}
+
+static int validate_mls_range(mls_range_t *range, validate_t *sens, validate_t *cats)
+{
+	if (validate_mls_level(&range->level[0], sens, cats))
+		goto bad;
+	if (validate_mls_level(&range->level[1], sens, cats))
+		goto bad;
+
+	return 0;
+
+	bad:
+	return -1;
+}
+
+static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[], int mls)
 {
 	if (validate_value(user->s.value, &flavors[SYM_USERS]))
 		goto bad;
@@ -300,6 +581,10 @@
 		goto bad;
 	if (validate_mls_semantic_level(&user->dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
 		goto bad;
+	if (mls && validate_mls_range(&user->exp_range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		goto bad;
+	if (mls && validate_mls_level(&user->exp_dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		goto bad;
 	if (user->bounds && validate_value(user->bounds, &flavors[SYM_USERS]))
 		goto bad;
 
@@ -310,32 +595,60 @@
 	return -1;
 }
 
-static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+static int validate_user_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_user_datum(margs->handle, d, margs->flavors, margs->mls);
+}
+
+static int validate_bool_datum(sepol_handle_t *handle, cond_bool_datum_t *boolean, validate_t flavors[])
+{
+	if (validate_value(boolean->s.value, &flavors[SYM_BOOLS]))
+		goto bad;
+
+	switch (boolean->state) {
+	case 0:
+	case 1:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (boolean->flags) {
+	case 0:
+	case COND_BOOL_FLAGS_TUNABLE:
+		break;
+	default:
+		goto bad;
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid bool datum");
+	return -1;
+}
+
+static int validate_bool_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_bool_datum(margs->handle, d, margs->flavors);
+}
+
+static int validate_datum_array_gaps(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
 {
 	unsigned int i;
 
 	for (i = 0; i < p->p_classes.nprim; i++) {
-		if (p->class_val_to_struct[i]) {
-			if (ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i))
-				goto bad;
-			if (validate_class_datum(handle, p->class_val_to_struct[i], flavors))
-				goto bad;
-		} else {
-			if (!ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i))
-				goto bad;
-		}
+		if (bool_xnor(p->class_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i)))
+			goto bad;
 	}
 
 	for (i = 0; i < p->p_roles.nprim; i++) {
-		if (p->role_val_to_struct[i]) {
-			if (ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i))
-				goto bad;
-			if (validate_role_datum(handle, p->role_val_to_struct[i], flavors))
-				goto bad;
-		} else {
-			if (!ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i))
-				goto bad;
-		}
+		if (bool_xnor(p->role_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i)))
+			goto bad;
 	}
 
 	/*
@@ -344,34 +657,68 @@
 	 */
 	if (p->policyvers < POLICYDB_VERSION_AVTAB || p->policyvers > POLICYDB_VERSION_PERMISSIVE) {
 		for (i = 0; i < p->p_types.nprim; i++) {
-			if (p->type_val_to_struct[i]) {
-				if (ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i))
-					goto bad;
-				if (validate_type_datum(handle, p->type_val_to_struct[i], flavors))
-					goto bad;
-			} else {
-				if (!ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i))
-					goto bad;
-			}
+			if (bool_xnor(p->type_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i)))
+				goto bad;
 		}
 	}
 
 	for (i = 0; i < p->p_users.nprim; i++) {
-		if (p->user_val_to_struct[i]) {
-			if (ebitmap_get_bit(&flavors[SYM_USERS].gaps, i))
-				goto bad;
-			if (validate_user_datum(handle, p->user_val_to_struct[i], flavors))
-				goto bad;
-		} else {
-			if (!ebitmap_get_bit(&flavors[SYM_USERS].gaps, i))
-				goto bad;
-		}
+		if (bool_xnor(p->user_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_USERS].gaps, i)))
+			goto bad;
+	}
+
+	for (i = 0; i < p->p_bools.nprim; i++) {
+		if (bool_xnor(p->bool_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_BOOLS].gaps, i)))
+			goto bad;
 	}
 
 	return 0;
 
 bad:
-	ERR(handle, "Invalid datum arrays");
+	ERR(handle, "Invalid datum array gaps");
+	return -1;
+}
+
+static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	symtab_datum_t *s = d;
+	uint32_t *nprim = (uint32_t *)args;
+
+	return !value_isvalid(s->value, *nprim);
+}
+
+static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	map_arg_t margs = { flavors, handle, p->mls };
+
+	if (hashtab_map(p->p_commons.table, validate_common_datum_wrapper, &margs))
+		goto bad;
+
+	if (hashtab_map(p->p_classes.table, validate_class_datum_wrapper, &margs))
+		goto bad;
+
+	if (hashtab_map(p->p_roles.table, validate_role_datum_wrapper, &margs))
+		goto bad;
+
+	if (hashtab_map(p->p_types.table, validate_type_datum_wrapper, &margs))
+		goto bad;
+
+	if (hashtab_map(p->p_users.table, validate_user_datum_wrapper, &margs))
+		goto bad;
+
+	if (p->mls && hashtab_map(p->p_levels.table, validate_level_datum, flavors))
+		goto bad;
+
+	if (hashtab_map(p->p_cats.table, validate_datum, &flavors[SYM_CATS]))
+		goto bad;
+
+	if (hashtab_map(p->p_bools.table, validate_bool_datum_wrapper, &margs))
+		goto bad;
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid datum array entries");
 	return -1;
 }
 
@@ -379,7 +726,7 @@
  * Functions to validate a kernel policydb
  */
 
-static int validate_avtab_key(avtab_key_t *key, validate_t flavors[])
+static int validate_avtab_key(avtab_key_t *key, int conditional, validate_t flavors[])
 {
 	if (validate_value(key->source_type, &flavors[SYM_TYPES]))
 		goto bad;
@@ -387,6 +734,23 @@
 		goto bad;
 	if (validate_value(key->target_class, &flavors[SYM_CLASSES]))
 		goto bad;
+	switch (0xFFF & key->specified) {
+	case AVTAB_ALLOWED:
+	case AVTAB_AUDITALLOW:
+	case AVTAB_AUDITDENY:
+	case AVTAB_TRANSITION:
+	case AVTAB_MEMBER:
+	case AVTAB_CHANGE:
+		break;
+	case AVTAB_XPERMS_ALLOWED:
+	case AVTAB_XPERMS_AUDITALLOW:
+	case AVTAB_XPERMS_DONTAUDIT:
+		if (conditional)
+			goto bad;
+		break;
+	default:
+		goto bad;
+	}
 
 	return 0;
 
@@ -394,15 +758,22 @@
 	return -1;
 }
 
-static int validate_avtab_key_wrapper(avtab_key_t *k,  __attribute__ ((unused)) avtab_datum_t *d, void *args)
+static int validate_avtab_key_and_datum(avtab_key_t *k, avtab_datum_t *d, void *args)
 {
 	validate_t *flavors = (validate_t *)args;
-	return validate_avtab_key(k, flavors);
+
+	if (validate_avtab_key(k, 0, flavors))
+		return -1;
+
+	if ((k->specified & AVTAB_TYPE) && validate_value(d->data, &flavors[SYM_TYPES]))
+		return -1;
+
+	return 0;
 }
 
 static int validate_avtab(sepol_handle_t *handle, avtab_t *avtab, validate_t flavors[])
 {
-	if (avtab_map(avtab, validate_avtab_key_wrapper, flavors)) {
+	if (avtab_map(avtab, validate_avtab_key_and_datum, flavors)) {
 		ERR(handle, "Invalid avtab");
 		return -1;
 	}
@@ -416,7 +787,7 @@
 
 	for (; cond_av; cond_av = cond_av->next) {
 		for (avtab_ptr = cond_av->node; avtab_ptr; avtab_ptr = avtab_ptr->next) {
-			if (validate_avtab_key(&avtab_ptr->key, flavors)) {
+			if (validate_avtab_key(&avtab_ptr->key, 1, flavors)) {
 				ERR(handle, "Invalid cond av list");
 				return -1;
 			}
@@ -426,7 +797,7 @@
 	return 0;
 }
 
-static int validate_avrules(sepol_handle_t *handle, avrule_t *avrule, validate_t flavors[])
+static int validate_avrules(sepol_handle_t *handle, avrule_t *avrule, int conditional, validate_t flavors[])
 {
 	class_perm_node_t *class;
 
@@ -440,6 +811,48 @@
 			if (validate_value(class->tclass, &flavors[SYM_CLASSES]))
 				goto bad;
 		}
+
+		switch(avrule->specified) {
+		case AVRULE_ALLOWED:
+		case AVRULE_AUDITALLOW:
+		case AVRULE_AUDITDENY:
+		case AVRULE_DONTAUDIT:
+		case AVRULE_TRANSITION:
+		case AVRULE_MEMBER:
+		case AVRULE_CHANGE:
+			break;
+		case AVRULE_NEVERALLOW:
+		case AVRULE_XPERMS_ALLOWED:
+		case AVRULE_XPERMS_AUDITALLOW:
+		case AVRULE_XPERMS_DONTAUDIT:
+		case AVRULE_XPERMS_NEVERALLOW:
+			if (conditional)
+				goto bad;
+			break;
+		default:
+			goto bad;
+		}
+
+		if (avrule->specified & AVRULE_XPERMS) {
+			if (!avrule->xperms)
+				goto bad;
+			switch (avrule->xperms->specified) {
+			case AVRULE_XPERMS_IOCTLFUNCTION:
+			case AVRULE_XPERMS_IOCTLDRIVER:
+				break;
+			default:
+				goto bad;
+			}
+		} else if (avrule->xperms)
+			goto bad;
+
+		switch(avrule->flags) {
+		case 0:
+		case RULE_SELF:
+			break;
+		default:
+			goto bad;
+		}
 	}
 
 	return 0;
@@ -475,9 +888,9 @@
 			goto bad;
 		if (validate_cond_av_list(handle, cond->false_list, flavors))
 			goto bad;
-		if (validate_avrules(handle, cond->avtrue_list, flavors))
+		if (validate_avrules(handle, cond->avtrue_list, 1, flavors))
 			goto bad;
-		if (validate_avrules(handle, cond->avfalse_list, flavors))
+		if (validate_avrules(handle, cond->avfalse_list, 1, flavors))
 			goto bad;
 		if (validate_bool_id_array(handle, cond->bool_ids, cond->nbools, &flavors[SYM_BOOLS]))
 			goto bad;
@@ -559,6 +972,77 @@
 	return 0;
 }
 
+static int validate_context(context_struct_t *con, validate_t flavors[], int mls)
+{
+	if (validate_value(con->user, &flavors[SYM_USERS]))
+		return -1;
+	if (validate_value(con->role, &flavors[SYM_ROLES]))
+		return -1;
+	if (validate_value(con->type, &flavors[SYM_TYPES]))
+		return -1;
+	if (mls && validate_mls_range(&con->range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		return -1;
+
+	return 0;
+}
+
+static int validate_ocontexts(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	ocontext_t *octx;
+	unsigned int i;
+
+	for (i = 0; i < OCON_NUM; i++) {
+		for (octx = p->ocontexts[i]; octx; octx = octx->next) {
+			if (validate_context(&octx->context[0], flavors, p->mls))
+				goto bad;
+
+			if (p->target_platform == SEPOL_TARGET_SELINUX) {
+				switch (i) {
+				case OCON_FS:
+				case OCON_NETIF:
+					if (validate_context(&octx->context[1], flavors, p->mls))
+						goto bad;
+					break;
+				case OCON_FSUSE:
+					switch (octx->v.behavior) {
+					case SECURITY_FS_USE_XATTR:
+					case SECURITY_FS_USE_TRANS:
+					case SECURITY_FS_USE_TASK:
+						break;
+					default:
+						goto bad;
+					}
+				}
+			}
+		}
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid ocontext");
+	return -1;
+}
+
+static int validate_genfs(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	genfs_t *genfs;
+	ocontext_t *octx;
+
+	for (genfs = p->genfs; genfs; genfs = genfs->next) {
+		for (octx = genfs->head; octx; octx = octx->next) {
+			if (validate_context(&octx->context[0], flavors, p->mls))
+				goto bad;
+		}
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid genfs");
+	return -1;
+}
+
 /*
  * Functions to validate a module policydb
  */
@@ -666,14 +1150,6 @@
 	return -1;
 }
 
-static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
-{
-	symtab_datum_t *s = d;
-	uint32_t *nprim = (uint32_t *)args;
-
-	return !value_isvalid(s->value, *nprim);
-}
-
 static int validate_symtabs(sepol_handle_t *handle, symtab_t symtabs[], validate_t flavors[])
 {
 	unsigned int i;
@@ -696,7 +1172,7 @@
 		for (decl = avrule_block->branch_list; decl != NULL; decl = decl->next) {
 			if (validate_cond_list(handle, decl->cond_list, flavors))
 				goto bad;
-			if (validate_avrules(handle, decl->avrules, flavors))
+			if (validate_avrules(handle, decl->avrules, 0, flavors))
 				goto bad;
 			if (validate_role_trans_rules(handle, decl->role_tr_rules, flavors))
 				goto bad;
@@ -713,6 +1189,14 @@
 			if (validate_symtabs(handle, decl->symtab, flavors))
 				goto bad;
 		}
+
+		switch (avrule_block->flags) {
+		case 0:
+		case AVRULE_OPTIONAL:
+			break;
+		default:
+			goto bad;
+		}
 	}
 
 	return 0;
@@ -722,6 +1206,71 @@
 	return -1;
 }
 
+static int validate_permissives(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	ebitmap_node_t *node;
+	unsigned i;
+
+	ebitmap_for_each_positive_bit(&p->permissive_map, node, i) {
+		if (validate_value(i, &flavors[SYM_TYPES]))
+			goto bad;
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid permissive type");
+	return -1;
+}
+
+static int validate_properties(sepol_handle_t *handle, policydb_t *p)
+{
+	switch (p->policy_type) {
+	case POLICY_KERN:
+		if (p->policyvers < POLICYDB_VERSION_MIN || p->policyvers > POLICYDB_VERSION_MAX)
+			goto bad;
+		break;
+	case POLICY_BASE:
+	case POLICY_MOD:
+		if (p->policyvers < MOD_POLICYDB_VERSION_MIN || p->policyvers > MOD_POLICYDB_VERSION_MAX)
+			goto bad;
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->target_platform) {
+	case SEPOL_TARGET_SELINUX:
+	case SEPOL_TARGET_XEN:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->mls) {
+	case 0:
+	case 1:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->handle_unknown) {
+	case SEPOL_DENY_UNKNOWN:
+	case SEPOL_REJECT_UNKNOWN:
+	case SEPOL_ALLOW_UNKNOWN:
+		break;
+	default:
+		goto bad;
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid policy property");
+	return -1;
+}
+
 static void validate_array_destroy(validate_t flavors[])
 {
 	unsigned int i;
@@ -741,6 +1290,9 @@
 	if (validate_array_init(p, flavors))
 		goto bad;
 
+	if (validate_properties(handle, p))
+		goto bad;
+
 	if (p->policy_type == POLICY_KERN) {
 		if (validate_avtab(handle, &p->te_avtab, flavors))
 			goto bad;
@@ -759,10 +1311,22 @@
 			goto bad;
 	}
 
+	if (validate_ocontexts(handle, p, flavors))
+		goto bad;
+
+	if (validate_genfs(handle, p, flavors))
+		goto bad;
+
 	if (validate_scopes(handle, p->scope, p->global))
 		goto bad;
 
-	if (validate_datum_arrays(handle, p, flavors))
+	if (validate_datum_array_gaps(handle, p, flavors))
+		goto bad;
+
+	if (validate_datum_array_entries(handle, p, flavors))
+		goto bad;
+
+	if (validate_permissives(handle, p, flavors))
 		goto bad;
 
 	validate_array_destroy(flavors);
diff --git a/libsepol/src/private.h b/libsepol/src/private.h
index 7128728..a8cc147 100644
--- a/libsepol/src/private.h
+++ b/libsepol/src/private.h
@@ -44,7 +44,12 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
 
-#define is_saturated(x) (x == (typeof(x))-1)
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+# define is_saturated(x) (x == (typeof(x))-1 || (x) > (1U << 16))
+#else
+# define is_saturated(x) (x == (typeof(x))-1)
+#endif
+
 #define zero_or_saturated(x) ((x == 0) || is_saturated(x))
 
 #define spaceship_cmp(a, b) (((a) > (b)) - ((a) < (b)))
@@ -78,3 +83,23 @@
 extern size_t put_entry(const void *ptr, size_t size, size_t n,
 		        struct policy_file *fp);
 extern int str_read(char **strp, struct policy_file *fp, size_t len);
+
+static inline void* mallocarray(size_t nmemb, size_t size) {
+	if (size && nmemb > (size_t)-1 / size) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	return malloc(nmemb * size);
+}
+
+#ifndef HAVE_REALLOCARRAY
+static inline void* reallocarray(void *ptr, size_t nmemb, size_t size) {
+	if (size && nmemb > (size_t)-1 / size) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	return realloc(ptr, nmemb * size);
+}
+#endif
diff --git a/libsepol/src/services.c b/libsepol/src/services.c
index 3407058..2972372 100644
--- a/libsepol/src/services.c
+++ b/libsepol/src/services.c
@@ -94,7 +94,7 @@
 		else
 			new_stack_len = stack_len * 2;
 
-		new_stack = realloc(stack, new_stack_len * sizeof(*stack));
+		new_stack = reallocarray(stack, new_stack_len, sizeof(*stack));
 		if (!new_stack) {
 			ERR(NULL, "unable to allocate stack space");
 			return;
@@ -449,8 +449,8 @@
 			else
 				new_expr_list_len = expr_list_len * 2;
 
-			new_expr_list = realloc(expr_list,
-					new_expr_list_len * sizeof(*expr_list));
+			new_expr_list = reallocarray(expr_list,
+					new_expr_list_len, sizeof(*expr_list));
 			if (!new_expr_list) {
 				ERR(NULL, "failed to allocate expr buffer stack");
 				rc = -ENOMEM;
@@ -712,7 +712,7 @@
 	 * Generate the same number of answer buffer entries as expression
 	 * buffers (as there will never be more).
 	 */
-	answer_list = malloc(expr_count * sizeof(*answer_list));
+	answer_list = mallocarray(expr_count, sizeof(*answer_list));
 	if (!answer_list) {
 		ERR(NULL, "failed to allocate answer stack");
 		rc = -ENOMEM;
@@ -797,7 +797,7 @@
 
 		for (x = 0; buffers[x] != NULL; x++) {
 			while (1) {
-				p = *r_buf + reason_buf_used;
+				p = *r_buf ? (*r_buf + reason_buf_used) : NULL;
 				len = snprintf(p, reason_buf_len - reason_buf_used,
 						"%s", buffers[x]);
 				if (len < 0 || len >= reason_buf_len - reason_buf_used) {
@@ -1553,7 +1553,7 @@
 		     cladatum2->comdatum->permissions.table)) {
 			ERR(NULL,
 			    " in the access vector definition "
-			    "for class %s\n", key);
+			    "for class %s", key);
 			return -1;
 		}
 	}
@@ -2163,7 +2163,7 @@
 	}
 	usercon.user = user->s.value;
 
-	mysids = malloc(maxnel * sizeof(sepol_security_id_t));
+	mysids = mallocarray(maxnel, sizeof(sepol_security_id_t));
 	if (!mysids) {
 		rc = -ENOMEM;
 		goto out;
@@ -2199,7 +2199,7 @@
 			} else {
 				maxnel += SIDS_NEL;
 				mysids2 =
-				    malloc(maxnel *
+				    mallocarray(maxnel,
 					   sizeof(sepol_security_id_t));
 
 				if (!mysids2) {
diff --git a/libsepol/src/sidtab.c b/libsepol/src/sidtab.c
index 255e072..adeae6e 100644
--- a/libsepol/src/sidtab.c
+++ b/libsepol/src/sidtab.c
@@ -15,6 +15,7 @@
 #include <sepol/policydb/sidtab.h>
 
 #include "flask.h"
+#include "private.h"
 
 #define SIDTAB_HASH(sid) \
 (sid & SIDTAB_HASH_MASK)
@@ -27,7 +28,7 @@
 {
 	int i;
 
-	s->htable = malloc(sizeof(sidtab_ptr_t) * SIDTAB_SIZE);
+	s->htable = mallocarray(SIDTAB_SIZE, sizeof(sidtab_ptr_t));
 	if (!s->htable)
 		return -ENOMEM;
 	for (i = 0; i < SIDTAB_SIZE; i++)
diff --git a/libsepol/src/user_record.c b/libsepol/src/user_record.c
index ac52006..404fa3a 100644
--- a/libsepol/src/user_record.c
+++ b/libsepol/src/user_record.c
@@ -4,6 +4,7 @@
 
 #include "user_internal.h"
 #include "debug.h"
+#include "private.h"
 
 struct sepol_user {
 	/* This user's name */
@@ -182,8 +183,9 @@
 	if (!role_cp)
 		goto omem;
 
-	roles_realloc = realloc(user->roles,
-				sizeof(char *) * (user->num_roles + 1));
+	roles_realloc = reallocarray(user->roles,
+				     user->num_roles + 1,
+				     sizeof(char *));
 	if (!roles_realloc)
 		goto omem;
 
@@ -265,7 +267,7 @@
 
 	unsigned int i;
 	const char **tmp_roles =
-	    (const char **)malloc(sizeof(char *) * user->num_roles);
+	    (const char **)mallocarray(user->num_roles, sizeof(char *));
 	if (!tmp_roles)
 		goto omem;
 
diff --git a/libsepol/src/users.c b/libsepol/src/users.c
index b895b7f..a740621 100644
--- a/libsepol/src/users.c
+++ b/libsepol/src/users.c
@@ -226,17 +226,17 @@
 		void *tmp_ptr;
 
 		/* Ensure reverse lookup array has enough space */
-		tmp_ptr = realloc(policydb->user_val_to_struct,
-				  (policydb->p_users.nprim +
-				   1) * sizeof(user_datum_t *));
+		tmp_ptr = reallocarray(policydb->user_val_to_struct,
+				  policydb->p_users.nprim + 1,
+				  sizeof(user_datum_t *));
 		if (!tmp_ptr)
 			goto omem;
 		policydb->user_val_to_struct = tmp_ptr;
 		policydb->user_val_to_struct[policydb->p_users.nprim] = NULL;
 
-		tmp_ptr = realloc(policydb->sym_val_to_name[SYM_USERS],
-				  (policydb->p_users.nprim +
-				   1) * sizeof(char *));
+		tmp_ptr = reallocarray(policydb->sym_val_to_name[SYM_USERS],
+				  policydb->p_users.nprim + 1,
+				  sizeof(char *));
 		if (!tmp_ptr)
 			goto omem;
 		policydb->sym_val_to_name[SYM_USERS] = tmp_ptr;
diff --git a/libsepol/src/util.c b/libsepol/src/util.c
index 902c63c..1cd1308 100644
--- a/libsepol/src/util.c
+++ b/libsepol/src/util.c
@@ -28,6 +28,8 @@
 #include <sepol/policydb/policydb.h>
 #include <sepol/policydb/util.h>
 
+#include "private.h"
+
 struct val_to_name {
 	unsigned int val;
 	char *name;
@@ -40,6 +42,8 @@
  * 0).  Return 0 on success, -1 on out of memory. */
 int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a)
 {
+	uint32_t *new;
+
 	if (cnt == NULL || a == NULL)
 		return -1;
 
@@ -48,17 +52,18 @@
 	 * than be smart about it, for now we realloc() the array each
 	 * time a new uint32_t is added! */
 	if (*a != NULL)
-		*a = (uint32_t *) realloc(*a, (*cnt + 1) * sizeof(uint32_t));
+		new = (uint32_t *) reallocarray(*a, *cnt + 1, sizeof(uint32_t));
 	else {			/* empty list */
 
 		*cnt = 0;
-		*a = (uint32_t *) malloc(sizeof(uint32_t));
+		new = (uint32_t *) malloc(sizeof(uint32_t));
 	}
-	if (*a == NULL) {
+	if (new == NULL) {
 		return -1;
 	}
-	(*a)[*cnt] = i;
+	new[*cnt] = i;
 	(*cnt)++;
+	*a = new;
 	return 0;
 }
 
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index b09551f..db244c0 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -2117,7 +2117,7 @@
 		 * buffer.  this would have been easier with C99's
 		 * dynamic arrays... */
 		rc = POLICYDB_ERROR;
-		dyn_buf = malloc(items * sizeof(*dyn_buf));
+		dyn_buf = mallocarray(items, sizeof(*dyn_buf));
 		if (!dyn_buf)
 			goto err;
 		buf = dyn_buf;
diff --git a/libsepol/tests/Makefile b/libsepol/tests/Makefile
index fc9bd1a..a72c327 100644
--- a/libsepol/tests/Makefile
+++ b/libsepol/tests/Makefile
@@ -1,3 +1,4 @@
+ENV ?= env
 M4 ?= m4
 MKDIR ?= mkdir
 EXE ?= libsepol-tests
@@ -44,10 +45,15 @@
 	rm -f $(objs) $(EXE)
 	rm -f $(policies)
 	rm -f policies/test-downgrade/policy.hi policies/test-downgrade/policy.lo
-	
 
+# mkdir is run in a clean environment created by env -i to avoid failing under ASan with:
+#
+#   ASan runtime does not come first in initial library list;
+#   you should either link runtime to your application or manually preload it with LD_PRELOAD
+#
+# when the source code is built with ASan
 test: $(EXE) $(policies)
-	$(MKDIR) -p policies/test-downgrade
+	$(ENV) -i $(MKDIR) -p policies/test-downgrade
 	../../checkpolicy/checkpolicy -M policies/test-cond/refpolicy-base.conf -o policies/test-downgrade/policy.hi	
 	./$(EXE)
 
diff --git a/mcstrans/Makefile b/mcstrans/Makefile
index c993a9f..b20279a 100644
--- a/mcstrans/Makefile
+++ b/mcstrans/Makefile
@@ -1,3 +1,9 @@
+PKG_CONFIG ?= pkg-config
+PCRE_MODULE := libpcre2-8
+PCRE_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PCRE_MODULE)) -DPCRE2_CODE_UNIT_WIDTH=8
+PCRE_LDLIBS := $(shell $(PKG_CONFIG) --libs $(PCRE_MODULE))
+export PCRE_MODULE PCRE_CFLAGS PCRE_LDLIBS
+
 all: 
 	$(MAKE) -C src 
 	$(MAKE) -C utils
diff --git a/mcstrans/src/Makefile b/mcstrans/src/Makefile
index 76ef055..ef51862 100644
--- a/mcstrans/src/Makefile
+++ b/mcstrans/src/Makefile
@@ -20,10 +20,10 @@
 all: $(PROG)
 
 $(PROG): $(PROG_OBJS) $(LIBSEPOLA)
-	$(CC) $(LDFLAGS) -pie -o $@ $^ -lselinux -lcap -lpcre $(LDLIBS_LIBSEPOLA)
+	$(CC) $(LDFLAGS) -pie -o $@ $^ -lselinux -lcap $(PCRE_LDLIBS) $(LDLIBS_LIBSEPOLA)
 
 %.o:  %.c 
-	$(CC) $(CFLAGS) -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -fPIE -c -o $@ $<
+	$(CC) $(CFLAGS) $(PCRE_CFLAGS) -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -fPIE -c -o $@ $<
 
 install: all
 	test -d $(DESTDIR)$(SBINDIR) || install -m 755 -d $(DESTDIR)$(SBINDIR)
diff --git a/mcstrans/src/mcscolor.c b/mcstrans/src/mcscolor.c
index a383885..9ff0ce2 100644
--- a/mcstrans/src/mcscolor.c
+++ b/mcstrans/src/mcscolor.c
@@ -11,6 +11,8 @@
 #include <syslog.h>
 #include <selinux/selinux.h>
 #include <selinux/context.h>
+
+#include "mcscolor.h"
 #include "mcstrans.h"
 
 /* Define data structures */
diff --git a/mcstrans/src/mcscolor.h b/mcstrans/src/mcscolor.h
new file mode 100644
index 0000000..c37fe6e
--- /dev/null
+++ b/mcstrans/src/mcscolor.h
@@ -0,0 +1,8 @@
+#ifndef __mcscolor_h__
+#define __mcscolor_h__
+
+extern void finish_context_colors(void);
+extern int init_colors(void);
+extern int raw_color(const char *raw, char **color_str);
+
+#endif
diff --git a/mcstrans/src/mcstrans.c b/mcstrans/src/mcstrans.c
index e92dfdd..d42760f 100644
--- a/mcstrans/src/mcstrans.c
+++ b/mcstrans/src/mcstrans.c
@@ -26,7 +26,7 @@
 #include <selinux/context.h>
 #include <syslog.h>
 #include <errno.h>
-#include <pcre.h>
+#include <pcre2.h>
 #include <ctype.h>
 #include <time.h>
 #include <sys/time.h>
@@ -36,7 +36,6 @@
 #include "mcstrans.h"
 
 #define N_BUCKETS 1453
-#define OVECCOUNT (512*3)
 
 #define log_error(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
 
@@ -82,9 +81,9 @@
 	affix_t *suffixes;
 	word_t *words;
 
-	pcre *prefix_regexp;
-	pcre *word_regexp;
-	pcre *suffix_regexp;
+	pcre2_code *prefix_regexp;
+	pcre2_code *word_regexp;
+	pcre2_code *suffix_regexp;
 
 	ebitmap_t def;
 
@@ -109,7 +108,7 @@
 	base_classification_t *base_classifications;
 	word_group_t *groups;
 
-	pcre *base_classification_regexp;
+	pcre2_code *base_classification_regexp;
 	struct domain *next;
 } domain_t;
 
@@ -136,7 +135,7 @@
 
 static cat_constraint_t *cat_constraints;
 
-unsigned int
+static unsigned int
 hash(const char *str) {
 	unsigned int hash = 5381;
 	int c;
@@ -213,7 +212,7 @@
 	return 0;
 }
 
-int
+static int
 parse_ebitmap(ebitmap_t *e, ebitmap_t *def, const char *raw) {
 	int rc = ebitmap_cpy(e, def);
 	if (rc < 0)
@@ -224,7 +223,7 @@
 	return 0;
 }
 
-mls_level_t *
+static mls_level_t *
 parse_raw(const char *raw) {
 	mls_level_t *mls = calloc(1, sizeof(mls_level_t));
 	if (!mls)
@@ -248,7 +247,7 @@
 	return NULL;
 }
 
-void
+static void
 destroy_word(word_t **list, word_t *word) {
 	if (!word) {
 		return;
@@ -267,7 +266,7 @@
 	free(word);
 }
 
-word_t *
+static word_t *
 create_word(word_t **list, const char *text) {
 	word_t *w = calloc(1, sizeof(word_t));
 	if (!w) {
@@ -291,7 +290,7 @@
 	return NULL;
 }
 
-void
+static void
 destroy_group(word_group_t **list, word_group_t *group) {
 	for (; list && *list; list = &(*list)->next) {
 		if (*list == group) {
@@ -317,14 +316,14 @@
 	free(group->name);
 	free(group->sword);
 	free(group->join);
-	pcre_free(group->prefix_regexp);
-	pcre_free(group->word_regexp);
-	pcre_free(group->suffix_regexp);
+	pcre2_code_free(group->prefix_regexp);
+	pcre2_code_free(group->word_regexp);
+	pcre2_code_free(group->suffix_regexp);
 	ebitmap_destroy(&group->def);
 	free(group);
 }
 
-word_group_t *
+static word_group_t *
 create_group(word_group_t **list, const char *name) {
 	word_group_t *group = calloc(1, sizeof(word_group_t));
 	if (!group)
@@ -357,7 +356,7 @@
 	return NULL;
 }
 
-void
+static void
 destroy_domain(domain_t *domain) {
 	int i;
         unsigned int rt = 0, tr = 0;
@@ -392,7 +391,7 @@
 		free(domain->base_classifications);
 		domain->base_classifications = next;
 	}
-	pcre_free(domain->base_classification_regexp);
+	pcre2_code_free(domain->base_classification_regexp);
 	while (domain->groups)
 		destroy_group(&domain->groups, domain->groups);
 	free(domain->name);
@@ -401,7 +400,7 @@
 	syslog(LOG_INFO, "cache sizes: tr = %u, rt = %u", tr, rt);
 }
 
-domain_t *
+static domain_t *
 create_domain(const char *name) {
 	domain_t *domain = calloc(1, sizeof(domain_t));
 	if (!domain) {
@@ -425,7 +424,7 @@
 	return NULL;
 }
 
-int
+static int
 add_word(word_group_t *group, char *raw, char *trans) {
 	if (strchr(trans,'-')) {
 		log_error("'%s'is invalid because '-' is illegal in modifiers.\n", trans);
@@ -451,7 +450,7 @@
 	return 0;
 }
 
-int
+static int
 add_constraint(char op, char *raw, char *tok) {
 	log_debug("%s\n", "add_constraint");
 	ebitmap_t empty;
@@ -521,7 +520,7 @@
 	return 0;
 }
 
-int
+static int
 violates_constraints(mls_level_t *l) {
 	int nbits;
 	sens_constraint_t *s;
@@ -563,7 +562,7 @@
 	return 0;
 }
 
-void
+static void
 destroy_sens_constraint(sens_constraint_t **list, sens_constraint_t *constraint) {
 	if (!constraint) {
 		return;
@@ -580,7 +579,7 @@
 	free(constraint);
 }
 
-void
+static void
 destroy_cat_constraint(cat_constraint_t **list, cat_constraint_t *constraint) {
 	if (!constraint) {
 		return;
@@ -663,7 +662,7 @@
 	return NULL;
 }
 
-char *
+static char *
 trim(char *str, const char *whitespace) {
 	char *p = str + strlen(str);
 
@@ -672,7 +671,7 @@
 	return str;
 }
 
-char *
+static char *
 triml(char *str, const char *whitespace) {
 	char *p = str;
 
@@ -681,7 +680,7 @@
 	return p;
 }
 
-int
+static int
 update(char **p, char *const val) {
 	free (*p);
 	*p = strdup(val);
@@ -692,7 +691,7 @@
 	return 0;
 }
 
-int
+static int
 append(affix_t **affixes, const char *val) {
 	affix_t *affix = calloc(1, sizeof(affix_t));
 	if (!affix) {
@@ -887,7 +886,7 @@
 	return(read_translations(selinux_translations_path()));
 }
 
-char *
+static char *
 extract_range(const char *incon) {
 	context_t con = context_new(incon);
 	if (!con) {
@@ -910,7 +909,7 @@
 	return r;
 }
 
-char *
+static char *
 new_context_str(const char *incon, const char *range) {
 	char *rcon = NULL;
 	context_t con = context_new(incon);
@@ -931,7 +930,7 @@
 	return NULL;
 }
 
-char *
+static char *
 find_in_hashtable(const char *range, domain_t *domain, context_map_node_t **table) {
 	char *trans = NULL;
 	context_map_t *map = find_in_table(table, range);
@@ -946,13 +945,6 @@
 	return trans;
 }
 
-void
-emit_whitespace(char*buffer, char *whitespace) {
-	strcat(buffer, "[");
-	strcat(buffer, whitespace);
-	strcat(buffer, "]");
-}
-
 static int
 string_size(const void *p1, const void *p2) {
 	return strlen(*(char **)p2) - strlen(*(char **)p1);
@@ -969,20 +961,22 @@
 	return (w2_len - w1_len);
 }
 
-void
-build_regexp(pcre **r, char *buffer) {
-	const char *error;
-	int error_offset;
+static void
+build_regexp(pcre2_code **r, char *buffer) {
+	int error;
+	PCRE2_SIZE error_offset;
 	if (*r)
-		pcre_free(*r);
-	*r = pcre_compile(buffer, PCRE_CASELESS, &error, &error_offset, NULL);
-	if (error) {
-		log_error("pcre=%s, error=%s\n", buffer, error ? error: "none");
+		pcre2_code_free(*r);
+	*r = pcre2_compile((PCRE2_SPTR8) buffer, PCRE2_ZERO_TERMINATED, PCRE2_CASELESS, &error, &error_offset, NULL);
+	if (!*r) {
+		PCRE2_UCHAR errbuf[256];
+		pcre2_get_error_message(error, errbuf, sizeof(errbuf));
+		log_error("pcre compilation of '%s' failed at offset %zu: %s\n", buffer, error_offset, errbuf);
 	}
 	buffer[0] = '\0';
 }
 
-int
+static int
 build_regexps(domain_t *domain) {
 	char buffer[1024 * 128];
 	buffer[0] = '\0';
@@ -1086,7 +1080,7 @@
 	return 0;
 }
 
-char *
+static char *
 compute_raw_from_trans(const char *level, domain_t *domain) {
 
 #ifdef DEBUG
@@ -1095,12 +1089,12 @@
 #endif
 
 	int rc = 0;
-	int ovector[OVECCOUNT];
+	pcre2_match_data *match_data = NULL;
 	word_group_t *g = NULL;
 	char *work = NULL;
 	char *r = NULL;
-	const char * match = NULL;
-	int work_len;
+	char *match = NULL;
+	size_t work_len;
 	mls_level_t *mraw = NULL;
 	ebitmap_t set, clear, tmp;
 
@@ -1121,11 +1115,20 @@
 	if (!domain->base_classification_regexp)
 		goto err;
 	log_debug(" compute_raw_from_trans work = %s\n", work);
-	rc = pcre_exec(domain->base_classification_regexp, 0, work, work_len, 0, PCRE_ANCHORED, ovector, OVECCOUNT);
+	match_data = pcre2_match_data_create_from_pattern(domain->base_classification_regexp, NULL);
+	if (!match_data) {
+		log_error("allocation error %s", strerror(errno));
+		goto err;
+	}
+	rc = pcre2_match(domain->base_classification_regexp, (PCRE2_SPTR8)work, work_len, 0, PCRE2_ANCHORED, match_data, NULL);
 	if (rc > 0) {
-		match = NULL;
-		pcre_get_substring(work, ovector, rc, 0, &match);
-		log_debug(" compute_raw_from_trans match = %s len = %u\n", match, strlen(match));
+		const PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
+		match = strndup(work + ovector[0], ovector[1] - ovector[0]);
+		if (!match) {
+			log_error("allocation error %s", strerror(errno));
+			goto err;
+		}
+		log_debug(" compute_raw_from_trans match = %s len = %zu\n", match, strlen(match));
 		base_classification_t *bc;
 		for (bc = domain->base_classifications; bc; bc = bc->next) {
 			if (!strcmp(bc->trans, match)) {
@@ -1145,12 +1148,23 @@
 		char *p=work + ovector[0] + ovector[1];
 		while (*p && (strchr(" 	", *p) != NULL))
 			*p++ = '#';
-		pcre_free((char *)match);
+
+		free(match);
 		match = NULL;
 	} else {
-		log_debug(" compute_raw_from_trans no base classification matched %s\n", level);
+		switch (rc) {
+		case PCRE2_ERROR_NOMATCH:
+			log_debug(" compute_raw_from_trans no base classification matched %s\n", level);
+			break;
+		default:
+			log_error("compute_raw_from_trans: base matching error for input '%s': %d\n", level, rc);
+			break;
+		}
 	}
 
+	pcre2_match_data_free(match_data);
+	match_data = NULL;
+
 	if (mraw == NULL) {
 		goto err;
 	}
@@ -1161,23 +1175,43 @@
 		change = 0;
 		for (g = domain->groups; g && !change && !complete; g = g->next) {
 			int prefix = 0, suffix = 0;
-			int prefix_offset = 0, prefix_len = 0;
-			int suffix_offset = 0, suffix_len = 0;
+			PCRE2_SIZE prefix_offset = 0, prefix_len = 0;
+			PCRE2_SIZE suffix_offset = 0, suffix_len = 0;
 			if (g->prefix_regexp) {
-				rc = pcre_exec(g->prefix_regexp, 0, work, work_len, 0, 0, ovector, OVECCOUNT);
+				match_data = pcre2_match_data_create_from_pattern(g->prefix_regexp, NULL);
+				if (!match_data) {
+					log_error("allocation error %s", strerror(errno));
+					goto err;
+				}
+				rc = pcre2_match(g->prefix_regexp, (PCRE2_SPTR8)work, work_len, 0, 0, match_data, NULL);
 				if (rc > 0) {
+					const PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
 					prefix = 1;
 					prefix_offset = ovector[0];
 					prefix_len = ovector[1] - ovector[0];
+				} else if (rc != PCRE2_ERROR_NOMATCH) {
+					log_error("compute_raw_from_trans: prefix matching error for input '%s': %d\n", level, rc);
 				}
+				pcre2_match_data_free(match_data);
+				match_data = NULL;
 			}
 			if (g->suffix_regexp) {
-				rc = pcre_exec(g->suffix_regexp, 0, work, work_len, 0, 0, ovector, OVECCOUNT);
+				match_data = pcre2_match_data_create_from_pattern(g->suffix_regexp, NULL);
+				if (!match_data) {
+					log_error("allocation error %s", strerror(errno));
+					goto err;
+				}
+				rc = pcre2_match(g->suffix_regexp, (PCRE2_SPTR8)work, work_len, 0, 0, match_data, NULL);
 				if (rc > 0) {
+					const PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
 					suffix = 1;
 					suffix_offset = ovector[0];
 					suffix_len = ovector[1] - ovector[0];
+				} else if (rc != PCRE2_ERROR_NOMATCH) {
+					log_error("compute_raw_from_trans: suffix matching error for input '%s': %d\n", level, rc);
 				}
+				pcre2_match_data_free(match_data);
+				match_data = NULL;
 			}
 
 /* anchors prefix ^, suffix $ */
@@ -1186,14 +1220,23 @@
 			     (g->suffixes && suffix)) &&
 			     g->word_regexp) {
 				char *s = work + prefix_offset + prefix_len;
-				int l = (suffix_len ? suffix_offset : work_len) - prefix_len - prefix_offset;
-				rc = pcre_exec(g->word_regexp, 0, s, l, 0, 0, ovector, OVECCOUNT);
+				PCRE2_SIZE len = (suffix_len ? suffix_offset : work_len) - prefix_len - prefix_offset;
+				match_data = pcre2_match_data_create_from_pattern(g->word_regexp, NULL);
+				if (!match_data) {
+					log_error("allocation error %s", strerror(errno));
+					goto err;
+				}
+				rc = pcre2_match(g->word_regexp, (PCRE2_SPTR8)s, len, 0, 0, match_data, NULL);
 				if (rc > 0) {
-					match = NULL;
-					pcre_get_substring(s, ovector, rc, 0, &match);
-					trim((char *)match, g->whitespace);
+					const PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data);
+					match = strndup(s + ovector[0], ovector[1] - ovector[0]);
+					if (!match) {
+						log_error("allocation error %s", strerror(errno));
+						goto err;
+					}
+					trim(match, g->whitespace);
 					if (*match) {
-						char *p = triml((char *)match, g->whitespace);
+						char *p = triml(match, g->whitespace);
 						while (p && *p) {
 							int plen = strlen(p);
 							unsigned int i;
@@ -1230,9 +1273,13 @@
 						memset(work + suffix_offset, '#', suffix_len);
 						memset(s + ovector[0], '#', ovector[1] - ovector[0]);
 					}
-					pcre_free((void *)match);
+					free(match);
 					match = NULL;
+				} else if (rc != PCRE2_ERROR_NOMATCH) {
+					log_error("compute_raw_from_trans: word matching error for input '%s' for substring '%s': %d\n", level, s, rc);
 				}
+				pcre2_match_data_free(match_data);
+				match_data = NULL;
 			}
 /* YYY */
 			complete=1;
@@ -1271,14 +1318,15 @@
 	mls_level_destroy(mraw);
 	free(mraw);
 	free(work);
-	pcre_free((void *)match);
+	free(match);
 	ebitmap_destroy(&tmp);
 	ebitmap_destroy(&set);
 	ebitmap_destroy(&clear);
+	pcre2_match_data_free(match_data);
 	return NULL;
 }
 
-char *
+static char *
 compute_trans_from_raw(const char *level, domain_t *domain) {
 
 #ifdef DEBUG
diff --git a/mcstrans/src/mcstrans.h b/mcstrans/src/mcstrans.h
index e5cda93..0addb32 100644
--- a/mcstrans/src/mcstrans.h
+++ b/mcstrans/src/mcstrans.h
@@ -6,4 +6,3 @@
 extern void finish_context_translations(void);
 extern int trans_context(const char *, char **);
 extern int untrans_context(const char *, char **);
-
diff --git a/mcstrans/src/mcstransd.c b/mcstrans/src/mcstransd.c
index 59c152e..536c0f3 100644
--- a/mcstrans/src/mcstransd.c
+++ b/mcstrans/src/mcstransd.c
@@ -16,6 +16,8 @@
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <sys/un.h>
+
+#include "mcscolor.h"
 #include "mcstrans.h"
 
 #ifdef UNUSED
@@ -43,15 +45,6 @@
 #define log_debug(fmt, ...) do {} while (0)
 #endif
 
-extern int init_translations(void);
-extern void finish_context_translations(void);
-extern int trans_context(const char *, char **);
-extern int untrans_context(const char *, char **);
-
-extern int init_colors(void);
-extern void finish_context_colors(void);
-extern int raw_color(const char *, char **);
-
 #define SETRANSD_PATHNAME "/sbin/mcstransd"
 
 /* name of program (for error messages) */
@@ -514,7 +507,7 @@
 
 }
 
-void dropprivs(void)
+static void dropprivs(void)
 {
 	cap_t new_caps;
 
diff --git a/mcstrans/utils/Makefile b/mcstrans/utils/Makefile
index 9dfe772..a48f4e7 100644
--- a/mcstrans/utils/Makefile
+++ b/mcstrans/utils/Makefile
@@ -14,13 +14,13 @@
 all: $(TARGETS)
 
 transcon: transcon.o ../src/mcstrans.o ../src/mls_level.o $(LIBSEPOLA)
-	$(CC) $(LDFLAGS) -o $@ $^ -lpcre -lselinux $(LDLIBS_LIBSEPOLA)
+	$(CC) $(LDFLAGS) -o $@ $^ $(PCRE_LDLIBS) -lselinux $(LDLIBS_LIBSEPOLA)
 
 untranscon: untranscon.o ../src/mcstrans.o ../src/mls_level.o $(LIBSEPOLA)
-	$(CC) $(LDFLAGS) -o $@ $^ -lpcre -lselinux $(LDLIBS_LIBSEPOLA)
+	$(CC) $(LDFLAGS) -o $@ $^ $(PCRE_LDLIBS) -lselinux $(LDLIBS_LIBSEPOLA)
 
 %.o:  %.c 
-	$(CC) $(CFLAGS) -D_GNU_SOURCE -I../src -fPIE -c -o $@ $<
+	$(CC) $(CFLAGS) $(PCRE_CFLAGS) -D_GNU_SOURCE -I../src -fPIE -c -o $@ $<
 
 install: all
 	-mkdir -p $(DESTDIR)$(SBINDIR)
diff --git a/policycoreutils/man/man5/selinux_config.5 b/policycoreutils/man/man5/selinux_config.5
index 58b42a0..f391bef 100644
--- a/policycoreutils/man/man5/selinux_config.5
+++ b/policycoreutils/man/man5/selinux_config.5
@@ -32,7 +32,7 @@
 .br
 \fBSELINUXTYPE = \fIpolicy_name\fR
 .br
-\fBREQUIREUSERS = \fI0\fR | \fI1\fR
+\fBREQUIRESEUSERS = \fI0\fR | \fI1\fR
 .br
 \fBAUTORELABEL = \fI0\fR | \fI1\fR
 .RE
diff --git a/policycoreutils/man/ru/man5/selinux_config.5 b/policycoreutils/man/ru/man5/selinux_config.5
index 40039e5..8c0db9a 100644
--- a/policycoreutils/man/ru/man5/selinux_config.5
+++ b/policycoreutils/man/ru/man5/selinux_config.5
@@ -34,7 +34,7 @@
 .br
 \fBSELINUXTYPE = \fIpolicy_name\fR
 .br
-\fBREQUIREUSERS = \fI0\fR | \fI1\fR
+\fBREQUIRESEUSERS = \fI0\fR | \fI1\fR
 .br
 \fBAUTORELABEL = \fI0\fR | \fI1\fR
 .RE
diff --git a/policycoreutils/newrole/Makefile b/policycoreutils/newrole/Makefile
index 4dedb7d..b3ccf67 100644
--- a/policycoreutils/newrole/Makefile
+++ b/policycoreutils/newrole/Makefile
@@ -91,3 +91,16 @@
 
 relabel: install
 	/sbin/restorecon $(DESTDIR)$(BINDIR)/newrole
+
+test-build-options:
+	$(MAKE) PAMH=y AUDITH=y AUDIT_LOG_PRIV=y NAMESPACE_PRIV=y clean newrole
+	$(MAKE) PAMH=y AUDITH=y AUDIT_LOG_PRIV=y NAMESPACE_PRIV=n clean newrole
+	$(MAKE) PAMH=y AUDITH=y AUDIT_LOG_PRIV=n NAMESPACE_PRIV=y clean newrole
+	$(MAKE) PAMH=y AUDITH=y AUDIT_LOG_PRIV=n NAMESPACE_PRIV=n clean newrole
+	$(MAKE) PAMH=y AUDITH=y AUDIT_LOG_PRIV=y NAMESPACE_PRIV=y clean newrole
+	$(MAKE) PAMH=y AUDITH=n AUDIT_LOG_PRIV=n NAMESPACE_PRIV=y clean newrole
+	$(MAKE) PAMH=y AUDITH=n AUDIT_LOG_PRIV=n NAMESPACE_PRIV=n clean newrole
+	$(MAKE) PAMH=n AUDITH=y AUDIT_LOG_PRIV=y NAMESPACE_PRIV=n clean newrole
+	$(MAKE) PAMH=n AUDITH=y AUDIT_LOG_PRIV=n NAMESPACE_PRIV=n clean newrole
+	$(MAKE) PAMH=n AUDITH=n AUDIT_LOG_PRIV=n NAMESPACE_PRIV=n clean newrole
+	$(MAKE) clean
diff --git a/policycoreutils/newrole/hashtab.c b/policycoreutils/newrole/hashtab.c
index bc50283..26d4f4c 100644
--- a/policycoreutils/newrole/hashtab.c
+++ b/policycoreutils/newrole/hashtab.c
@@ -44,7 +44,7 @@
 
 int hashtab_insert(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum)
 {
-	int hvalue;
+	unsigned int hvalue;
 	hashtab_ptr_t prev, cur, newnode;
 
 	if (!h)
@@ -83,7 +83,7 @@
 		   void (*destroy) (hashtab_key_t k,
 				    hashtab_datum_t d, void *args), void *args)
 {
-	int hvalue;
+	unsigned int hvalue;
 	hashtab_ptr_t cur, last;
 
 	if (!h)
@@ -115,7 +115,7 @@
 hashtab_datum_t hashtab_search(hashtab_t h, const_hashtab_key_t key)
 {
 
-	int hvalue;
+	unsigned int hvalue;
 	hashtab_ptr_t cur;
 
 	if (!h)
@@ -160,8 +160,9 @@
 		int (*apply) (hashtab_key_t k,
 			      hashtab_datum_t d, void *args), void *args)
 {
-	unsigned int i, ret;
+	unsigned int i;
 	hashtab_ptr_t cur;
+	int ret;
 
 	if (!h)
 		return HASHTAB_SUCCESS;
diff --git a/policycoreutils/newrole/newrole.c b/policycoreutils/newrole/newrole.c
index 31b51c5..ae37d72 100644
--- a/policycoreutils/newrole/newrole.c
+++ b/policycoreutils/newrole/newrole.c
@@ -100,7 +100,6 @@
 #endif
 
 #define DEFAULT_PATH "/usr/bin:/bin"
-#define DEFAULT_CONTEXT_SIZE 255	/* first guess at context size */
 
 extern char **environ;
 
@@ -115,7 +114,7 @@
  *
  * Returns malloc'd memory
  */
-static char *build_new_range(char *newlevel, const char *range)
+static char *build_new_range(const char *newlevel, const char *range)
 {
 	char *newrangep = NULL;
 	const char *tmpptr;
@@ -166,7 +165,7 @@
 #include <security/pam_appl.h>	/* for PAM functions */
 #include <security/pam_misc.h>	/* for misc_conv PAM utility function */
 
-const char *service_name = "newrole";
+static const char *service_name = "newrole";
 
 /* authenticate_via_pam()
  *
@@ -182,7 +181,7 @@
  * program.  This is the only function in this program that makes PAM
  * calls.
  */
-int authenticate_via_pam(const char *ttyn, pam_handle_t * pam_handle)
+static int authenticate_via_pam(const char *ttyn, pam_handle_t * pam_handle)
 {
 
 	int result = 0;		/* set to 0 (not authenticated) by default */
@@ -230,14 +229,13 @@
 
 static unsigned int reqsymhash(hashtab_t h, const_hashtab_key_t key)
 {
-	char *p, *keyp;
+	const char *p;
 	size_t size;
 	unsigned int val;
 
 	val = 0;
-	keyp = (char *)key;
-	size = strlen(keyp);
-	for (p = keyp; ((size_t) (p - keyp)) < size; p++)
+	size = strlen(key);
+	for (p = key; ((size_t) (p - key)) < size; p++)
 		val =
 		    (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
 	return val & (h->size - 1);
@@ -335,6 +333,14 @@
 
 #define PASSWORD_PROMPT _("Password:")	/* prompt for getpass() */
 
+static void memzero(void *ptr, size_t size)
+{
+	volatile unsigned char * volatile p = ptr;
+	while (size--) {
+		*p++ = '\0';
+	}
+}
+
 /* authenticate_via_shadow_passwd()
  *
  * in:     uname - the calling user's user name
@@ -348,11 +354,12 @@
  * This function uses the shadow passwd file to thenticate the user running
  * this program.
  */
-int authenticate_via_shadow_passwd(const char *uname)
+static int authenticate_via_shadow_passwd(const char *uname)
 {
 	struct spwd *p_shadow_line;
 	char *unencrypted_password_s;
 	char *encrypted_password_s;
+	int ret;
 
 	setspent();
 	p_shadow_line = getspnam(uname);
@@ -370,10 +377,18 @@
 	}
 
 	/* Use crypt() to encrypt user's input password. */
+	errno = 0;
 	encrypted_password_s = crypt(unencrypted_password_s,
 				     p_shadow_line->sp_pwdp);
-	memset(unencrypted_password_s, 0, strlen(unencrypted_password_s));
-	return (!strcmp(encrypted_password_s, p_shadow_line->sp_pwdp));
+	memzero(unencrypted_password_s, strlen(unencrypted_password_s));
+	if (errno || !encrypted_password_s) {
+		fprintf(stderr, _("Cannot encrypt password.\n"));
+		return 0;
+	}
+
+	ret = !strcmp(encrypted_password_s, p_shadow_line->sp_pwdp);
+	memzero(encrypted_password_s, strlen(encrypted_password_s));
+	return ret;
 }
 #endif				/* if/else USE_PAM */
 
@@ -623,7 +638,7 @@
  * This function will set the uid values to be that of caller's uid, and
  * will drop any privilege which may have been raised.
  */
-static int transition_to_caller_uid()
+static int transition_to_caller_uid(void)
 {
 	uid_t uid = getuid();
 
@@ -850,7 +865,6 @@
 		case 'V':
 			printf("newrole: %s version %s\n", PACKAGE, VERSION);
 			exit(0);
-			break;
 		case 'p':
 			*preserve_environment = 1;
 			break;
diff --git a/policycoreutils/run_init/open_init_pty.c b/policycoreutils/run_init/open_init_pty.c
index 150cb45..19101c5 100644
--- a/policycoreutils/run_init/open_init_pty.c
+++ b/policycoreutils/run_init/open_init_pty.c
@@ -244,7 +244,7 @@
 	rb_init(&inbuf, inbuf_mem, sizeof(inbuf_mem));
 	rb_init(&outbuf, outbuf_mem, sizeof(outbuf_mem));
 
-	if (argc == 1) {
+	if (argc < 2) {
 		printf("usage: %s PROGRAM [ARGS]...\n", argv[0]);
 		exit(1);
 	}
diff --git a/policycoreutils/run_init/run_init.c b/policycoreutils/run_init/run_init.c
index 545490a..ce49978 100644
--- a/policycoreutils/run_init/run_init.c
+++ b/policycoreutils/run_init/run_init.c
@@ -86,8 +86,6 @@
 				  /* The file containing the context to run 
 				   * the scripts under.                     */
 
-int authenticate_via_pam(const struct passwd *);
-
 /* authenticate_via_pam()
  *
  * in:     p_passwd_line - struct containing data from our user's line in 
@@ -104,7 +102,7 @@
  *
  */
 
-int authenticate_via_pam(const struct passwd *p_passwd_line)
+static int authenticate_via_pam(const struct passwd *p_passwd_line)
 {
 
 	int result = 0;		/* our result, set to 0 (not authenticated) by default */
@@ -169,8 +167,6 @@
 
 #define PASSWORD_PROMPT _("Password:")	/* prompt for getpass() */
 
-int authenticate_via_shadow_passwd(const struct passwd *);
-
 /* authenticate_via_shadow_passwd()
  *
  * in:     p_passwd_line - struct containing data from our user's line in 
@@ -187,7 +183,7 @@
  *
  */
 
-int authenticate_via_shadow_passwd(const struct passwd *p_passwd_line)
+static int authenticate_via_shadow_passwd(const struct passwd *p_passwd_line)
 {
 
 	struct spwd *p_shadow_line;	/* struct derived from shadow passwd file line */
@@ -238,7 +234,7 @@
  * return:	0 When success
  *		-1 When failure
  */
-int authenticate_user(void)
+static int authenticate_user(void)
 {
 
 #define INITLEN 255
@@ -303,7 +299,7 @@
  * out:		The CONTEXT associated with the context.
  * return:	0 on success, -1 on failure.
  */
-int get_init_context(char **context)
+static int get_init_context(char **context)
 {
 
 	FILE *fp;
diff --git a/policycoreutils/scripts/fixfiles b/policycoreutils/scripts/fixfiles
index 6fb12e0..7df4303 100755
--- a/policycoreutils/scripts/fixfiles
+++ b/policycoreutils/scripts/fixfiles
@@ -109,6 +109,7 @@
 BOOTTIME=""
 VERBOSE="-p"
 FORCEFLAG=""
+THREADS=""
 RPMFILES=""
 PREFC=""
 RESTORE_MODE=""
@@ -152,7 +153,7 @@
     shift
     LogReadOnly
     for m in `echo $FILESYSTEMSRW`; do
-	find $m -mount -newermt $DATE -print0 2>/dev/null | ${RESTORECON} ${FORCEFLAG} ${VERBOSE} $* -i -0 -f -
+	find $m -mount -newermt $DATE -print0 2>/dev/null | ${RESTORECON} ${FORCEFLAG} ${VERBOSE} ${THREADS} $* -i -0 -f -
     done;
 }
 
@@ -196,7 +197,7 @@
 		  esac; \
 	       fi; \
 	    done | \
-	${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -i -R -f -; \
+	${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -i -R -f -; \
 	rm -f ${TEMPFILE} ${PREFCTEMPFILE}
 fi
 }
@@ -234,11 +235,11 @@
 case "$RESTORE_MODE" in
     RPMFILES)
 	for i in `echo "$RPMFILES" | sed 's/,/ /g'`; do
-	    rpmlist $i | ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -i -R -f -
+	    rpmlist $i | ${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -i -R -f -
 	done
     ;;
     FILEPATH)
-	${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -R -- "$FILEPATH"
+	${RESTORECON} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -R -- "$FILEPATH"
     ;;
     *)
 	if [ -n "${FILESYSTEMSRW}" ]; then
@@ -246,7 +247,7 @@
 	    echo "${OPTION}ing `echo ${FILESYSTEMSRW}`"
 
 	    if [ -z "$BIND_MOUNT_FILESYSTEMS" ]; then
-	        ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -q ${FC} ${FILESYSTEMSRW}
+	        ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -q ${THREADS} ${FC} ${FILESYSTEMSRW}
 	    else
 	        # we bind mount so we can fix the labels of files that have already been
 	        # mounted over
@@ -256,7 +257,7 @@
 
 	            mkdir -p "${TMP_MOUNT}${m}" || exit 1
 	            mount --bind "${m}" "${TMP_MOUNT}${m}" || exit 1
-	            ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} $* -q ${FC} -r "${TMP_MOUNT}" "${TMP_MOUNT}${m}"
+	            ${SETFILES} ${VERBOSE} ${EXCLUDEDIRS} ${FORCEFLAG} ${THREADS} $* -q ${FC} -r "${TMP_MOUNT}" "${TMP_MOUNT}${m}"
 	            umount "${TMP_MOUNT}${m}" || exit 1
 	            rm -rf "${TMP_MOUNT}" || echo "Error cleaning up."
 	        done;
@@ -329,8 +330,9 @@
 	fi
 	> /.autorelabel || exit $?
 	[ -z "$FORCEFLAG" ] || echo -n "$FORCEFLAG " >> /.autorelabel
-	[ -z "$BOOTTIME" ] || echo -N $BOOTTIME >> /.autorelabel
-	[ -z "$BIND_MOUNT_FILESYSTEMS" ] || echo "-M" >> /.autorelabel
+	[ -z "$BOOTTIME" ] || echo -n "-N $BOOTTIME " >> /.autorelabel
+	[ -z "$BIND_MOUNT_FILESYSTEMS" ] || echo -n "-M " >> /.autorelabel
+	[ -z "$THREADS" ] || echo -n "$THREADS " >> /.autorelabel
 	# Force full relabel if SELinux is not enabled
 	selinuxenabled || echo -F > /.autorelabel
 	echo "System will relabel on next boot"
@@ -342,17 +344,17 @@
 }
 usage() {
 	echo $"""
-Usage: $0 [-v] [-F] [-M] [-f] relabel
+Usage: $0 [-v] [-F] [-M] [-f] [-T nthreads] relabel
 or
-Usage: $0 [-v] [-F] [-B | -N time ] { check | restore | verify }
+Usage: $0 [-v] [-F] [-B | -N time ] [-T nthreads] { check | restore | verify }
 or
-Usage: $0 [-v] [-F] { check | restore | verify } dir/file ...
+Usage: $0 [-v] [-F] [-T nthreads] { check | restore | verify } dir/file ...
 or
-Usage: $0 [-v] [-F] -R rpmpackage[,rpmpackage...] { check | restore | verify }
+Usage: $0 [-v] [-F] [-T nthreads] -R rpmpackage[,rpmpackage...] { check | restore | verify }
 or
-Usage: $0 [-v] [-F] -C PREVIOUS_FILECONTEXT { check | restore | verify }
+Usage: $0 [-v] [-F] [-T nthreads] -C PREVIOUS_FILECONTEXT { check | restore | verify }
 or
-Usage: $0 [-F] [-M] [-B] onboot
+Usage: $0 [-F] [-M] [-B] [-T nthreads] onboot
 """
 }
 
@@ -371,7 +373,7 @@
 }
 
 # See how we were called.
-while getopts "N:BC:FfR:l:vM" i; do
+while getopts "N:BC:FfR:l:vMT:" i; do
     case "$i" in
 	B)
 		BOOTTIME=`/bin/who -b | awk '{print $3}'`
@@ -406,6 +408,9 @@
 	f)
 		fullFlag=1
 		;;
+	T)
+		THREADS="-T $OPTARG"
+		;;
 	*)
 	    usage
 	    exit 1
diff --git a/policycoreutils/scripts/fixfiles.8 b/policycoreutils/scripts/fixfiles.8
index c4e894e..9a317d9 100644
--- a/policycoreutils/scripts/fixfiles.8
+++ b/policycoreutils/scripts/fixfiles.8
@@ -6,22 +6,22 @@
 .na
 
 .B fixfiles
-.I [\-v] [\-F] [-M] [\-f] relabel
+.I [\-v] [\-F] [-M] [\-f] [\-T nthreads] relabel
 
 .B fixfiles
-.I [\-v] [\-F] { check | restore | verify } dir/file ...
+.I [\-v] [\-F] [\-T nthreads] { check | restore | verify } dir/file ...
 
 .B fixfiles
-.I [\-v] [\-F] [\-B | \-N time ] { check | restore | verify }
+.I [\-v] [\-F] [\-B | \-N time ] [\-T nthreads] { check | restore | verify }
 
 .B fixfiles 
-.I [\-v] [\-F] \-R rpmpackagename[,rpmpackagename...] { check | restore | verify }
+.I [\-v] [\-F] [\-T nthreads] \-R rpmpackagename[,rpmpackagename...] { check | restore | verify }
 
 .B fixfiles
-.I [\-v] [\-F] \-C PREVIOUS_FILECONTEXT  { check | restore | verify }
+.I [\-v] [\-F] [\-T nthreads] \-C PREVIOUS_FILECONTEXT  { check | restore | verify }
 
 .B fixfiles
-.I [-F] [-M] [-B] onboot
+.I [-F] [-M] [-B] [\-T nthreads] onboot
 
 .ad
 
@@ -76,6 +76,11 @@
 .B -v
 Modify verbosity from progress to verbose. (Run restorecon with \-v instead of \-p)
 
+.TP
+.B \-T nthreads
+Use parallel relabeling, see
+.B setfiles(8)
+
 .SH "ARGUMENTS"
 One of:
 .TP 
diff --git a/policycoreutils/secon/secon.c b/policycoreutils/secon/secon.c
index a0957d0..d624fa1 100644
--- a/policycoreutils/secon/secon.c
+++ b/policycoreutils/secon/secon.c
@@ -333,6 +333,9 @@
 		opts->from_type = OPTS_FROM_CUR;
 
 	if (opts->from_type == OPTS_FROM_ARG) {
+		if (!argv[0])
+			errx(EXIT_FAILURE, "No argument given");
+
 		opts->f.arg = argv[0];
 
 		if (xstreq(argv[0], "-"))
diff --git a/policycoreutils/semodule/semodule.8 b/policycoreutils/semodule/semodule.8
index 18d4f70..d1735d2 100644
--- a/policycoreutils/semodule/semodule.8
+++ b/policycoreutils/semodule/semodule.8
@@ -23,6 +23,13 @@
 .B \-B, \-\-build
 force a rebuild of policy (also reloads unless \-n is used)
 .TP
+.B \-\-rebuild-if-modules-changed
+Force a rebuild of the policy if any changes to module content are detected
+(by comparing with checksum from the last transaction).  One can use this
+instead of \-B to ensure that any changes to the module store done by an
+external tool (e.g. a package manager) are applied, while automatically
+skipping the rebuild if there are no new changes.
+.TP
 .B \-D, \-\-disable_dontaudit
 Temporarily remove dontaudits from policy.  Reverts whenever policy is rebuilt
 .TP
@@ -95,6 +102,9 @@
 .B  \-H,\-\-hll
 Extract module as an HLL file. This only affects the \-\-extract option and
 only modules listed in \-\-extract after this option.
+.TP
+.B  \-m,\-\-checksum
+Add SHA256 checksum of modules to the list output.
 
 .SH EXAMPLE
 .nf
@@ -130,6 +140,9 @@
 # Write the HLL version of puppet and the CIL version of wireshark
 # modules at priority 400 to the current working directory
 $ semodule \-X 400 \-\-hll \-E puppet \-\-cil \-E wireshark
+# Check whether a module in "localmodule.pp" file is same as installed module "localmodule"
+$ /usr/libexec/selinux/hll/pp localmodule.pp | sha256sum
+$ semodule -l -m | grep localmodule
 .fi
 
 .SH SEE ALSO
diff --git a/policycoreutils/semodule/semodule.c b/policycoreutils/semodule/semodule.c
index c815f01..1ed8e69 100644
--- a/policycoreutils/semodule/semodule.c
+++ b/policycoreutils/semodule/semodule.c
@@ -47,6 +47,7 @@
 static int reload;
 static int no_reload;
 static int build;
+static int check_ext_changes;
 static int disable_dontaudit;
 static int preserve_tunables;
 static int ignore_module_cache;
@@ -57,6 +58,7 @@
 static char *store;
 static char *store_root;
 int extract_cil = 0;
+static int checksum = 0;
 
 extern char *optarg;
 extern int optind;
@@ -147,6 +149,10 @@
 	printf("  -S,--store-path  use an alternate path for the policy store root\n");
 	printf("  -c, --cil extract module as cil. This only affects module extraction.\n");
 	printf("  -H, --hll extract module as hll. This only affects module extraction.\n");
+	printf("  -m, --checksum   print module checksum (SHA256).\n");
+	printf("      --rebuild-if-modules-changed\n"
+	       "                   force policy rebuild if module content changed since\n"
+	       "                   last rebuild (based on checksum)\n");
 }
 
 /* Sets the global mode variable to new_mode, but only if no other
@@ -178,6 +184,7 @@
 static void parse_command_line(int argc, char **argv)
 {
 	static struct option opts[] = {
+		{"rebuild-if-modules-changed", 0, NULL, '\0'},
 		{"store", required_argument, NULL, 's'},
 		{"base", required_argument, NULL, 'b'},
 		{"help", 0, NULL, 'h'},
@@ -200,19 +207,31 @@
 		{"disable", required_argument, NULL, 'd'},
 		{"path", required_argument, NULL, 'p'},
 		{"store-path", required_argument, NULL, 'S'},
+		{"checksum", 0, NULL, 'm'},
 		{NULL, 0, NULL, 0}
 	};
 	int extract_selected = 0;
 	int cil_hll_set = 0;
-	int i;
+	int i, longind;
 	verbose = 0;
 	reload = 0;
 	no_reload = 0;
+	check_ext_changes = 0;
 	priority = 400;
 	while ((i =
-		getopt_long(argc, argv, "s:b:hi:l::vr:u:RnNBDCPX:e:d:p:S:E:cH", opts,
-			    NULL)) != -1) {
+		getopt_long(argc, argv, "s:b:hi:l::vr:u:RnNBDCPX:e:d:p:S:E:cHm",
+			    opts, &longind)) != -1) {
 		switch (i) {
+		case '\0':
+			switch(longind) {
+			case 0: /* --rebuild-if-modules-changed */
+				check_ext_changes = 1;
+				break;
+			default:
+				usage(argv[0]);
+				exit(1);
+			}
+			break;
 		case 'b':
 			fprintf(stderr, "The --base option is deprecated. Use --install instead.\n");
 			set_mode(INSTALL_M, optarg);
@@ -287,6 +306,9 @@
 		case 'd':
 			set_mode(DISABLE_M, optarg);
 			break;
+		case 'm':
+			checksum = 1;
+			break;
 		case '?':
 		default:{
 				usage(argv[0]);
@@ -294,13 +316,13 @@
 			}
 		}
 	}
-	if ((build || reload) && num_commands) {
+	if ((build || reload || check_ext_changes) && num_commands) {
 		fprintf(stderr,
 			"build or reload should not be used with other commands\n");
 		usage(argv[0]);
 		exit(1);
 	}
-	if (num_commands == 0 && reload == 0 && build == 0) {
+	if (num_commands == 0 && reload == 0 && build == 0 && check_ext_changes == 0) {
 		fprintf(stderr, "At least one mode must be specified.\n");
 		usage(argv[0]);
 		exit(1);
@@ -338,6 +360,42 @@
 	}
 }
 
+/* Get module checksum */
+static char *hash_module_data(const char *module_name, const int prio) {
+	semanage_module_key_t *modkey = NULL;
+	char *hash_str = NULL;
+	void *hash = NULL;
+	size_t hash_len = 0;
+	int result;
+
+	result = semanage_module_key_create(sh, &modkey);
+	if (result != 0) {
+		goto cleanup;
+	}
+
+	result = semanage_module_key_set_name(sh, modkey, module_name);
+	if (result != 0) {
+		goto cleanup;
+	}
+
+	result = semanage_module_key_set_priority(sh, modkey, prio);
+	if (result != 0) {
+		goto cleanup;
+	}
+
+	result = semanage_module_compute_checksum(sh, modkey, 1, &hash_str,
+						  &hash_len);
+	if (result != 0) {
+		goto cleanup;
+	}
+
+cleanup:
+	free(hash);
+	semanage_module_key_destroy(sh, modkey);
+	free(modkey);
+	return hash_str;
+}
+
 int main(int argc, char *argv[])
 {
 	int i, commit = 0;
@@ -353,7 +411,7 @@
 
 	cil_set_log_level(CIL_ERR + verbose);
 
-	if (build)
+	if (build || check_ext_changes)
 		commit = 1;
 
 	sh = semanage_handle_create();
@@ -392,7 +450,7 @@
 		}
 	}
 
-	if (build) {
+	if (build || check_ext_changes) {
 		if ((result = semanage_begin_transaction(sh)) < 0) {
 			fprintf(stderr, "%s:  Could not begin transaction:  %s\n",
 				argv[0], errno ? strerror(errno) : "");
@@ -546,6 +604,8 @@
 				int modinfos_len = 0;
 				semanage_module_info_t *m = NULL;
 				int j = 0;
+				char *module_checksum = NULL;
+				uint16_t pri = 0;
 
 				if (verbose) {
 					printf
@@ -570,7 +630,18 @@
 						result = semanage_module_info_get_name(sh, m, &name);
 						if (result != 0) goto cleanup_list;
 
-						printf("%s\n", name);
+						result = semanage_module_info_get_priority(sh, m, &pri);
+						if (result != 0) goto cleanup_list;
+
+						printf("%s", name);
+						if (checksum) {
+							module_checksum = hash_module_data(name, pri);
+							if (module_checksum) {
+								printf(" %s", module_checksum);
+								free(module_checksum);
+							}
+						}
+						printf("\n");
 					}
 				}
 				else if (strcmp(mode_arg, "full") == 0) {
@@ -585,12 +656,16 @@
 					}
 
 					/* calculate column widths */
-					size_t column[4] = { 0, 0, 0, 0 };
+					size_t column[5] = { 0, 0, 0, 0, 0 };
 
 					/* fixed width columns */
 					column[0] = sizeof("000") - 1;
 					column[3] = sizeof("disabled") - 1;
 
+					result = semanage_module_compute_checksum(sh, NULL, 0, NULL,
+										  &column[4]);
+					if (result != 0) goto cleanup_list;
+
 					/* variable width columns */
 					const char *tmp = NULL;
 					size_t size;
@@ -607,12 +682,11 @@
 						if (result != 0) goto cleanup_list;
 
 						size = strlen(tmp);
-						if (size > column[3]) column[3] = size;
+						if (size > column[2]) column[2] = size;
 					}
 
 					/* print out each module */
 					for (j = 0; j < modinfos_len; j++) {
-						uint16_t pri = 0;
 						const char *name = NULL;
 						int enabled = 0;
 						const char *lang_ext = NULL;
@@ -631,11 +705,20 @@
 						result = semanage_module_info_get_lang_ext(sh, m, &lang_ext);
 						if (result != 0) goto cleanup_list;
 
-						printf("%0*u %-*s %-*s %-*s\n",
+						printf("%0*u %-*s %-*s %-*s",
 							(int)column[0], pri,
 							(int)column[1], name,
 							(int)column[2], lang_ext,
 							(int)column[3], enabled ? "" : "disabled");
+						if (checksum) {
+							module_checksum = hash_module_data(name, pri);
+							if (module_checksum) {
+								printf(" %-*s", (int)column[4], module_checksum);
+								free(module_checksum);
+							}
+						}
+						printf("\n");
+
 					}
 				}
 				else {
@@ -740,6 +823,8 @@
 			semanage_set_reload(sh, 0);
 		if (build)
 			semanage_set_rebuild(sh, 1);
+		if (check_ext_changes)
+			semanage_set_check_ext_changes(sh, 1);
 		if (disable_dontaudit)
 			semanage_set_disable_dontaudit(sh, 1);
 		else if (build)
diff --git a/policycoreutils/sestatus/sestatus.c b/policycoreutils/sestatus/sestatus.c
index ceee0d5..7dcc994 100644
--- a/policycoreutils/sestatus/sestatus.c
+++ b/policycoreutils/sestatus/sestatus.c
@@ -35,7 +35,7 @@
 
 extern char *selinux_mnt;
 
-int cmp_cmdline(const char *command, int pid)
+static int cmp_cmdline(const char *command, int pid)
 {
 
 	char buf[BUFSIZE];
@@ -59,7 +59,7 @@
 		return 0;
 }
 
-int pidof(const char *command)
+static int pidof(const char *command)
 {
 /* inspired by killall5.c from psmisc */
 	char stackpath[PATH_MAX + 1], *p;
@@ -92,7 +92,7 @@
 	return ret;
 }
 
-void load_checks(char *pc[], int *npc, char *fc[], int *nfc)
+static void load_checks(char *pc[], int *npc, char *fc[], int *nfc)
 {
 
 	FILE *fp = fopen(CONF, "r");
@@ -168,11 +168,9 @@
 	return;
 }
 
-void printf_tab(const char *outp)
+static void printf_tab(const char *outp)
 {
-	char buf[20];
-	snprintf(buf, sizeof(buf), "%%-%us", COL);
-	printf(buf, outp);
+	printf("%-*s", COL, outp);
 
 }
 
diff --git a/policycoreutils/setfiles/Makefile b/policycoreutils/setfiles/Makefile
index 63d8185..d7670a8 100644
--- a/policycoreutils/setfiles/Makefile
+++ b/policycoreutils/setfiles/Makefile
@@ -6,7 +6,7 @@
 AUDITH ?= $(shell test -f /usr/include/libaudit.h && echo y)
 
 CFLAGS ?= -g -Werror -Wall -W
-override LDLIBS += -lselinux -lsepol
+override LDLIBS += -lselinux -lsepol -lpthread
 
 ifeq ($(AUDITH), y)
 	override CFLAGS += -DUSE_AUDIT
diff --git a/policycoreutils/setfiles/restore.c b/policycoreutils/setfiles/restore.c
index 9d688c6..e9ae33a 100644
--- a/policycoreutils/setfiles/restore.c
+++ b/policycoreutils/setfiles/restore.c
@@ -29,7 +29,7 @@
 
 	opts->hnd = selabel_open(SELABEL_CTX_FILE, selinux_opts, 3);
 	if (!opts->hnd) {
-		perror(opts->selabel_opt_path);
+		perror(opts->selabel_opt_path ? opts->selabel_opt_path : selinux_file_context_path());
 		exit(1);
 	}
 
@@ -72,7 +72,7 @@
 	}
 }
 
-int process_glob(char *name, struct restore_opts *opts)
+int process_glob(char *name, struct restore_opts *opts, size_t nthreads)
 {
 	glob_t globbuf;
 	size_t i = 0;
@@ -91,8 +91,9 @@
 			continue;
 		if (len > 0 && strcmp(&globbuf.gl_pathv[i][len], "/..") == 0)
 			continue;
-		rc = selinux_restorecon(globbuf.gl_pathv[i],
-					opts->restorecon_flags);
+		rc = selinux_restorecon_parallel(globbuf.gl_pathv[i],
+						 opts->restorecon_flags,
+						 nthreads);
 		if (rc < 0)
 			errors = rc;
 	}
diff --git a/policycoreutils/setfiles/restore.h b/policycoreutils/setfiles/restore.h
index ac6ad68..bb35a1d 100644
--- a/policycoreutils/setfiles/restore.h
+++ b/policycoreutils/setfiles/restore.h
@@ -49,7 +49,7 @@
 void restore_init(struct restore_opts *opts);
 void restore_finish(void);
 void add_exclude(const char *directory);
-int process_glob(char *name, struct restore_opts *opts);
+int process_glob(char *name, struct restore_opts *opts, size_t nthreads);
 extern char **exclude_list;
 
 #endif
diff --git a/policycoreutils/setfiles/restorecon.8 b/policycoreutils/setfiles/restorecon.8
index 668486f..e07db2c 100644
--- a/policycoreutils/setfiles/restorecon.8
+++ b/policycoreutils/setfiles/restorecon.8
@@ -33,6 +33,8 @@
 .RB [ \-W ]
 .RB [ \-I | \-D ]
 .RB [ \-x ]
+.RB [ \-T
+.IR nthreads ]
 
 .SH "DESCRIPTION"
 This manual page describes the
@@ -160,6 +162,13 @@
 .B restorecon
 from crossing file system boundaries.
 .TP
+.BI \-T \ nthreads
+use up to
+.I nthreads
+threads.  Specify 0 to create as many threads as there are available
+CPU cores; 1 to use only a single thread (default); or any positive
+number to use the given number of threads (if possible).
+.TP
 .SH "ARGUMENTS"
 .IR pathname \ ...
 The pathname for the file(s) to be relabeled.
diff --git a/policycoreutils/setfiles/setfiles.8 b/policycoreutils/setfiles/setfiles.8
index 4d28bc9..15f939d 100644
--- a/policycoreutils/setfiles/setfiles.8
+++ b/policycoreutils/setfiles/setfiles.8
@@ -19,6 +19,8 @@
 .RB [ \-W ]
 .RB [ \-F ]
 .RB [ \-I | \-D ]
+.RB [ \-T
+.IR nthreads ]
 .I spec_file
 .IR pathname \ ...
 
@@ -161,6 +163,13 @@
 option of GNU
 .B find
 produces input suitable for this mode.
+.TP
+.BI \-T \ nthreads
+use up to
+.I nthreads
+threads.  Specify 0 to create as many threads as there are available
+CPU cores; 1 to use only a single thread (default); or any positive
+number to use the given number of threads (if possible).
 
 .SH "ARGUMENTS"
 .TP
diff --git a/policycoreutils/setfiles/setfiles.c b/policycoreutils/setfiles/setfiles.c
index f018d16..ab7016a 100644
--- a/policycoreutils/setfiles/setfiles.c
+++ b/policycoreutils/setfiles/setfiles.c
@@ -1,4 +1,5 @@
 #include "restore.h"
+#include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <stdio_ext.h>
@@ -34,20 +35,20 @@
 {
 	if (iamrestorecon) {
 		fprintf(stderr,
-			"usage:  %s [-iIDFmnprRv0x] [-e excludedir] pathname...\n"
-			"usage:  %s [-iIDFmnprRv0x] [-e excludedir] -f filename\n",
+			"usage:  %s [-iIDFmnprRv0xT] [-e excludedir] pathname...\n"
+			"usage:  %s [-iIDFmnprRv0xT] [-e excludedir] -f filename\n",
 			name, name);
 	} else {
 		fprintf(stderr,
-			"usage:  %s [-diIDlmnpqvEFW] [-e excludedir] [-r alt_root_path] [-c policyfile] spec_file pathname...\n"
-			"usage:  %s [-diIDlmnpqvEFW] [-e excludedir] [-r alt_root_path] [-c policyfile] spec_file -f filename\n"
-			"usage:  %s -s [-diIDlmnpqvFW] spec_file\n",
+			"usage:  %s [-diIDlmnpqvEFWT] [-e excludedir] [-r alt_root_path] [-c policyfile] spec_file pathname...\n"
+			"usage:  %s [-diIDlmnpqvEFWT] [-e excludedir] [-r alt_root_path] [-c policyfile] spec_file -f filename\n"
+			"usage:  %s -s [-diIDlmnpqvFWT] spec_file\n",
 			name, name, name);
 	}
 	exit(-1);
 }
 
-void set_rootpath(const char *arg)
+static void set_rootpath(const char *arg)
 {
 	if (strlen(arg) == 1 && strncmp(arg, "/", 1) == 0) {
 		fprintf(stderr, "%s:  invalid alt_rootpath: %s\n",
@@ -64,7 +65,7 @@
 	}
 }
 
-int canoncon(char **contextp)
+static int canoncon(char **contextp)
 {
 	char *context = *contextp, *tmpcon;
 	int rc = 0;
@@ -144,12 +145,12 @@
 	int opt, i = 0;
 	const char *input_filename = NULL;
 	int use_input_file = 0;
-	char *buf = NULL;
-	size_t buf_len;
+	char *buf = NULL, *endptr;
+	size_t buf_len, nthreads = 1;
 	const char *base;
 	int errors = 0;
-	const char *ropts = "e:f:hiIDlmno:pqrsvFRW0x";
-	const char *sopts = "c:de:f:hiIDlmno:pqr:svEFR:W0";
+	const char *ropts = "e:f:hiIDlmno:pqrsvFRW0xT:";
+	const char *sopts = "c:de:f:hiIDlmno:pqr:svEFR:W0T:";
 	const char *opts;
 	union selinux_callback cb;
 
@@ -162,6 +163,10 @@
 	policyfile = NULL;
 
 	r_opts.abort_on_error = 0;
+	if (!argv[0]) {
+		fprintf(stderr, "Called without required program name!\n");
+		exit(-1);
+	}
 	r_opts.progname = strdup(argv[0]);
 	if (!r_opts.progname) {
 		fprintf(stderr, "%s:  Out of memory!\n", argv[0]);
@@ -370,6 +375,11 @@
 				usage(argv[0]);
                         }
                         break;
+		case 'T':
+			nthreads = strtoull(optarg, &endptr, 10);
+			if (*optarg == '\0' || *endptr != '\0')
+				usage(argv[0]);
+			break;
 		case 'h':
 		case '?':
 			usage(argv[0]);
@@ -417,7 +427,7 @@
 
 		altpath = argv[optind];
 		optind++;
-	} else if (argc == 1)
+	} else if (argc < 2)
 		usage(argv[0]);
 
 	/* Set selabel_open options. */
@@ -448,13 +458,13 @@
 			buf[len - 1] = 0;
 			if (!strcmp(buf, "/"))
 				r_opts.mass_relabel = SELINUX_RESTORECON_MASS_RELABEL;
-			errors |= process_glob(buf, &r_opts) < 0;
+			errors |= process_glob(buf, &r_opts, nthreads) < 0;
 		}
 		if (strcmp(input_filename, "-") != 0)
 			fclose(f);
 	} else {
 		for (i = optind; i < argc; i++)
-			errors |= process_glob(argv[i], &r_opts) < 0;
+			errors |= process_glob(argv[i], &r_opts, nthreads) < 0;
 	}
 
 	maybe_audit_mass_relabel(r_opts.mass_relabel, errors);
diff --git a/python/audit2allow/sepolgen-ifgen-attr-helper.c b/python/audit2allow/sepolgen-ifgen-attr-helper.c
index f010c95..6f3ba96 100644
--- a/python/audit2allow/sepolgen-ifgen-attr-helper.c
+++ b/python/audit2allow/sepolgen-ifgen-attr-helper.c
@@ -56,7 +56,7 @@
 	return 0;
 }
 
-int render_access_mask(uint32_t av, avtab_key_t *key, policydb_t *policydbp,
+static int render_access_mask(uint32_t av, avtab_key_t *key, policydb_t *policydbp,
 		       FILE *fp)
 {
 	struct val_to_name v;
@@ -111,7 +111,7 @@
 	FILE *fp;
 };
 
-int output_avrule(avtab_key_t *key, avtab_datum_t *datum, void *args)
+static int output_avrule(avtab_key_t *key, avtab_datum_t *datum, void *args)
 {
 	struct callback_data *cb_data = (struct callback_data *)args;
 
@@ -217,7 +217,7 @@
 
 }
 
-void usage(char *progname)
+static void usage(char *progname)
 {
 	printf("usage: %s out_file [policy_file]\n", progname);
 }
diff --git a/python/semanage/semanage-fcontext.8 b/python/semanage/semanage-fcontext.8
index 49635ba..1ebf085 100644
--- a/python/semanage/semanage-fcontext.8
+++ b/python/semanage/semanage-fcontext.8
@@ -3,7 +3,7 @@
 semanage\-fcontext \- SELinux Policy Management file context tool
 
 .SH "SYNOPSIS"
-.B semanage fcontext [\-h] [\-n] [\-N] [\-S STORE] [ \-\-add ( \-t TYPE \-f FTYPE \-r RANGE \-s SEUSER | \-e EQUAL ) FILE_SPEC ) | \-\-delete ( \-t TYPE \-f FTYPE | \-e EQUAL ) FILE_SPEC ) | \-\-deleteall  | \-\-extract  | \-\-list [\-C] | \-\-modify ( \-t TYPE \-f FTYPE \-r RANGE \-s SEUSER | \-e EQUAL ) FILE_SPEC ) ]
+.B semanage fcontext [\-h] [\-n] [\-N] [\-S STORE] [ \-\-add ( \-t TYPE \-f FTYPE \-r RANGE \-s SEUSER | \-e EQUAL ) FILE_SPEC | \-\-delete ( \-t TYPE \-f FTYPE | \-e EQUAL ) FILE_SPEC | \-\-deleteall  | \-\-extract  | \-\-list [\-C] | \-\-modify ( \-t TYPE \-f FTYPE \-r RANGE \-s SEUSER | \-e EQUAL ) FILE_SPEC ]
 
 .SH "DESCRIPTION"
 semanage is used to configure certain elements of
diff --git a/python/sepolgen/src/sepolgen/refparser.py b/python/sepolgen/src/sepolgen/refparser.py
index e611637..1bb9056 100644
--- a/python/sepolgen/src/sepolgen/refparser.py
+++ b/python/sepolgen/src/sepolgen/refparser.py
@@ -261,7 +261,7 @@
     return t
 
 def t_FILENAME(t):
-    r'\"[a-zA-Z0-9_\-\+\.\$\*~ :]+\"'
+    r'\"[a-zA-Z0-9_\-\+\.\$\*~ :\[\]]+\"'
     # Handle any keywords
     t.type = reserved.get(t.value,'FILENAME')
     return t
diff --git a/sandbox/seunshare.c b/sandbox/seunshare.c
index d626e98..8917a0f 100644
--- a/sandbox/seunshare.c
+++ b/sandbox/seunshare.c
@@ -89,7 +89,7 @@
 /**
  * If the user sends a siginto to seunshare, kill the child's session
  */
-void handler(int sig) {
+static void handler(int sig) {
 	if (child > 0) kill(-child,sig);
 }
 
diff --git a/scripts/ci/fedora-test-runner.sh b/scripts/ci/fedora-test-runner.sh
index f817499..3ce2c3a 100755
--- a/scripts/ci/fedora-test-runner.sh
+++ b/scripts/ci/fedora-test-runner.sh
@@ -36,7 +36,7 @@
     libcap-devel \
     libcap-ng-devel \
     pam-devel \
-    pcre-devel \
+    pcre2-devel \
     xmlto \
     python3-devel \
     ruby-devel \
diff --git a/scripts/oss-fuzz.sh b/scripts/oss-fuzz.sh
index 16cc3c0..72d275e 100755
--- a/scripts/oss-fuzz.sh
+++ b/scripts/oss-fuzz.sh
@@ -32,7 +32,7 @@
 flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER -fsanitize=fuzzer-no-link"
 
 export CC=${CC:-clang}
-export CFLAGS=${CFLAGS:-$flags}
+export CFLAGS="${CFLAGS:-$flags} -I$DESTDIR/usr/include -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
 
 export CXX=${CXX:-clang++}
 export CXXFLAGS=${CXXFLAGS:-$flags}
@@ -49,11 +49,24 @@
 # shellcheck disable=SC2016
 make -C libsepol V=1 LD_SONAME_FLAGS='-soname,$(LIBSO),--version-script=$(LIBMAP)' -j"$(nproc)" install
 
+## secilc fuzzer ##
+
 # CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by
 # the compiler/linker so they shouldn't be quoted
 # shellcheck disable=SC2086
-$CC $CFLAGS -I"$DESTDIR/usr/include" -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c
+$CC $CFLAGS -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c
 # shellcheck disable=SC2086
 $CXX $CXXFLAGS $LIB_FUZZING_ENGINE secilc-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/secilc-fuzzer"
 
 zip -r "$OUT/secilc-fuzzer_seed_corpus.zip" secilc/test
+
+## binary policy fuzzer ##
+
+# CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by
+# the compiler/linker so they shouldn't be quoted
+# shellcheck disable=SC2086
+$CC $CFLAGS -c -o binpolicy-fuzzer.o libsepol/fuzz/binpolicy-fuzzer.c
+# shellcheck disable=SC2086
+$CXX $CXXFLAGS $LIB_FUZZING_ENGINE binpolicy-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/binpolicy-fuzzer"
+
+zip -j "$OUT/binpolicy-fuzzer_seed_corpus.zip" libsepol/fuzz/policy.bin
diff --git a/secilc/docs/cil_file_labeling_statements.md b/secilc/docs/cil_file_labeling_statements.md
index ed7b7bf..73f7388 100644
--- a/secilc/docs/cil_file_labeling_statements.md
+++ b/secilc/docs/cil_file_labeling_statements.md
@@ -36,11 +36,13 @@
 <col width="44%" />
 <col width="55%" />
 </colgroup>
-<tbody>
+<thead>
 <tr class="odd">
 <td align="left"><p><strong>keyword</strong></p></td>
 <td align="left"><p><strong>file_contexts entry</strong></p></td>
 </tr>
+</thead>
+<tbody>
 <tr class="even">
 <td align="left"><p><code>file</code></p></td>
 <td align="left"><p><code>--</code></p></td>
@@ -185,7 +187,7 @@
 **Statement definition:**
 
 ```secil
-    (genfscon fsname path context_id)
+    (genfscon fsname path [file_type] context_id)
 ```
 
 **Where:**
@@ -209,6 +211,10 @@
 <td align="left"><p>If <code>fsname</code> is <code>proc</code>, then the partial path (see examples). For all other types this must be ‘<code>/</code>’.</p></td>
 </tr>
 <tr class="even">
+<td align="left"><p><code>file_type</code></p></td>
+<td align="left"><p>Optional keyword representing a file type. Valid values are the same as in [`filecon`](cil_file_labeling_statements.md#filecon) rules.</p></td>
+</tr>
+<tr class="odd">
 <td align="left"><p><code>context_id</code></p></td>
 <td align="left"><p>A previously declared <code>context</code> identifier or an anonymous security context (<code>user role type levelrange</code>), the range MUST be defined whether the policy is MLS/MCS enabled or not.</p></td>
 </tr>