diff mbox series

[V4,3/3] selinux-testsuite: Add BPF support to binder test

Message ID 20190917145640.25629-4-richard_c_haines@btinternet.com (mailing list archive)
State Changes Requested
Headers show
Series selinux-testsuite: Add BPF tests | expand

Commit Message

Richard Haines Sept. 17, 2019, 2:56 p.m. UTC
Add BPF map & prog functions to test binder security_binder_transfer_file()

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
---
 policy/Makefile                 |   2 +-
 policy/test_binder_bpf.te       |  73 ++++++++++++++++++++
 tests/binder/Makefile           |   5 ++
 tests/binder/binder_common.c    |  10 +--
 tests/binder/binder_common.h    |  17 ++++-
 tests/binder/client.c           |  28 ++++++--
 tests/binder/manager.c          |   2 +-
 tests/binder/service_provider.c | 118 ++++++++++++++++++++++++--------
 tests/bpf/Makefile              |   2 +-
 tests/bpf/test                  |  84 ++++++++++++++++++++++-
 10 files changed, 297 insertions(+), 44 deletions(-)
 create mode 100644 policy/test_binder_bpf.te
diff mbox series

Patch

diff --git a/policy/Makefile b/policy/Makefile
index 4ca5486..d72eb62 100644
--- a/policy/Makefile
+++ b/policy/Makefile
@@ -72,7 +72,7 @@  TARGETS += test_sctp.te
 endif
 
 ifeq ($(shell grep -q bpf $(POLDEV)/include/support/all_perms.spt && echo true),true)
-TARGETS += test_bpf.te test_fdreceive_bpf.te
+TARGETS += test_bpf.te test_fdreceive_bpf.te test_binder_bpf.te
 endif
 
 ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6))
diff --git a/policy/test_binder_bpf.te b/policy/test_binder_bpf.te
new file mode 100644
index 0000000..c545846
--- /dev/null
+++ b/policy/test_binder_bpf.te
@@ -0,0 +1,73 @@ 
+####### Policy for testing BPF file descriptor transfers via binder #########
+
+attribute binderbpfdomain;
+
+#
+################################## Manager ##################################
+#
+type test_binder_bpf_mgr_t;
+domain_type(test_binder_bpf_mgr_t)
+unconfined_runs_test(test_binder_bpf_mgr_t)
+typeattribute test_binder_bpf_mgr_t testdomain;
+typeattribute test_binder_bpf_mgr_t binderdomain;
+allow test_binder_bpf_mgr_t test_binder_bpf_client_t:binder { transfer };
+allow test_binder_bpf_mgr_t test_binder_client_no_bpf_perm_t:binder { transfer };
+allow test_binder_bpf_mgr_t device_t:chr_file { ioctl open read write };
+allow_map(test_binder_bpf_mgr_t, device_t, chr_file)
+allow test_binder_bpf_mgr_t self:binder { set_context_mgr };
+# For writing to flag file:
+allow test_binder_bpf_mgr_t test_file_t:fifo_file { rw_file_perms };
+
+#
+########################### Service Provider ################################
+#
+type test_binder_bpf_provider_t;
+domain_type(test_binder_bpf_provider_t)
+unconfined_runs_test(test_binder_bpf_provider_t)
+typeattribute test_binder_bpf_provider_t testdomain;
+typeattribute test_binder_bpf_provider_t binderbpfdomain;
+allow test_binder_bpf_provider_t test_binder_bpf_mgr_t:binder { call transfer };
+allow test_binder_bpf_provider_t device_t:chr_file { ioctl open read write };
+allow_map(test_binder_bpf_provider_t, device_t, chr_file)
+# For writing to flag file:
+allow test_binder_bpf_provider_t test_file_t:fifo_file { rw_file_perms };
+# For testing BPF map fd transfer:
+allow test_binder_bpf_provider_t self:bpf { map_create map_read map_write prog_load prog_run };
+allow test_binder_bpf_provider_t self:capability { sys_resource };
+allow test_binder_bpf_provider_t self:process { setrlimit };
+
+#
+################################# Client ####################################
+#
+type test_binder_bpf_client_t;
+domain_type(test_binder_bpf_client_t)
+unconfined_runs_test(test_binder_bpf_client_t)
+typeattribute test_binder_bpf_client_t testdomain;
+typeattribute test_binder_bpf_client_t binderbpfdomain;
+allow test_binder_bpf_client_t test_binder_bpf_provider_t:binder { call impersonate };
+allow test_binder_bpf_client_t test_binder_bpf_mgr_t:binder { call };
+allow test_binder_bpf_client_t test_binder_bpf_provider_t:fd { use };
+allow test_binder_bpf_client_t device_t:chr_file { getattr ioctl open read write };
+allow_map(test_binder_bpf_client_t, device_t, chr_file)
+# For testing BPF map fd transfer:
+allow test_binder_bpf_client_t test_binder_bpf_provider_t:bpf { map_read map_write prog_load prog_run };
+
+#
+######################## Client no BPF perms #############################
+#
+type test_binder_client_no_bpf_perm_t;
+domain_type(test_binder_client_no_bpf_perm_t)
+unconfined_runs_test(test_binder_client_no_bpf_perm_t)
+typeattribute test_binder_client_no_bpf_perm_t testdomain;
+typeattribute test_binder_client_no_bpf_perm_t binderbpfdomain;
+allow test_binder_client_no_bpf_perm_t test_binder_bpf_provider_t:binder { call impersonate };
+allow test_binder_client_no_bpf_perm_t test_binder_bpf_mgr_t:binder { call };
+allow test_binder_client_no_bpf_perm_t test_binder_bpf_provider_t:fd { use };
+allow test_binder_client_no_bpf_perm_t device_t:chr_file { getattr ioctl open read write };
+allow_map(test_binder_client_no_bpf_perm_t, device_t, chr_file)
+
+#
+########### Allow these domains to be entered from sysadm domain ############
+#
+miscfiles_domain_entry_test_files(binderbpfdomain)
+userdom_sysadm_entry_spec_domtrans_to(binderbpfdomain)
diff --git a/tests/binder/Makefile b/tests/binder/Makefile
index 32f9a83..e78ad16 100644
--- a/tests/binder/Makefile
+++ b/tests/binder/Makefile
@@ -10,6 +10,11 @@  CFLAGS += -DHAVE_BINDERFS
 TARGETS += check_binderfs
 endif
 
+ifneq (,$(findstring -DHAVE_BPF,$(CFLAGS)))
+	DEPS += ../bpf/bpf_common.c ../bpf/bpf_common.h
+	LDLIBS += -lbpf
+endif
+
 all: $(TARGETS)
 
 clean:
diff --git a/tests/binder/binder_common.c b/tests/binder/binder_common.c
index a240453..224238b 100644
--- a/tests/binder/binder_common.c
+++ b/tests/binder/binder_common.c
@@ -3,13 +3,15 @@ 
  * the raw ioctl commands to test the SELinux binder permissions:
  *     set_context_mgr, call, transfer, impersonate.
  *
+ * If configured, the BPF permissions are also tested.
+ *
  * Using binder test policy the following will be validated:
  *    security_binder_set_context_mgr() binder { set_context_mgr }
  *    security_binder_transaction()     binder { call impersonate }
  *    security_binder_transfer_binder() binder { transfer }
  *    security_binder_transfer_file()   fd { use }
- *
- * TODO security_binder_transfer_file() uses BPF if configured in kernel.
+ *					bpf { map_create map_read map_write };
+ *					bpf { prog_load prog_run };
  */
 
 #include "binder_common.h"
@@ -67,8 +69,8 @@  void print_trans_data(const struct binder_transaction_data *txn_in)
 	case TEST_SERVICE_GET:
 		printf("\tcode: TEST_SERVICE_GET\n");
 		break;
-	case TEST_SERVICE_SEND_CLIENT_SP_FD:
-		printf("\tcode: TEST_SERVICE_SEND_CLIENT_SP_FD\n");
+	case TEST_SERVICE_SEND_FD:
+		printf("\tcode: TEST_SERVICE_SEND_FD\n");
 		break;
 	default:
 		printf("Unknown binder_transaction_data->code: %x\n",
diff --git a/tests/binder/binder_common.h b/tests/binder/binder_common.h
index bcf9e0c..30edc75 100644
--- a/tests/binder/binder_common.h
+++ b/tests/binder/binder_common.h
@@ -15,6 +15,9 @@ 
 #if HAVE_BINDERFS
 #include <linux/android/binderfs.h>
 #endif
+#if HAVE_BPF
+#include "../bpf/bpf_common.h"
+#endif
 
 #define BINDER_DEV "/dev/binder"
 #define BINDERFS_DEV "/dev/binderfs"
@@ -26,12 +29,20 @@ 
 /* These are the Binder txn->code values used by the Service Provider, Client
  * and Manager to request/retrieve a binder handle or file descriptor.
  */
-#define TEST_SERVICE_ADD		240616 /* Sent by Service Provider */
-#define TEST_SERVICE_GET		290317 /* Sent by Client */
-#define TEST_SERVICE_SEND_CLIENT_SP_FD	120419 /* Sent by Client */
+#define TEST_SERVICE_ADD	240616 /* Sent by Service Provider */
+#define TEST_SERVICE_GET	290317 /* Sent by Client */
+#define TEST_SERVICE_SEND_FD	311019 /* Sent by Client */
 
 bool verbose;
 
 const char *cmd_name(uint32_t cmd);
 void print_trans_data(const struct binder_transaction_data *txn_in);
 int binder_write(int fd, void *data, size_t len);
+
+enum {
+	BINDER_FD,
+	BPF_MAP_FD,
+	BPF_PROG_FD,
+	BPF_TEST
+} fd_type;
+char *fd_type_str;
diff --git a/tests/binder/client.c b/tests/binder/client.c
index e4e2a61..4965563 100644
--- a/tests/binder/client.c
+++ b/tests/binder/client.c
@@ -6,7 +6,7 @@  static int transactions_complete;
 static void usage(char *progname)
 {
 	fprintf(stderr,
-		"usage:  %s [-c] [-n] [-r replies] [-v]\n"
+		"usage:  %s [-c] [-n] [-r replies] [-m|-p] [-v]\n"
 		"Where:\n\t"
 		"-c  Use the number of replies for the BR_TRANSACTION_COMPLETE"
 		" count.\n\t"
@@ -15,6 +15,8 @@  static void usage(char *progname)
 		"    It can be the number of BR_TRANSACTION_COMPLETE if\n\t"
 		"    the -c option is set or number of times to issue the\n\t"
 		"    ioctl - BINDER_WRITE_READ command if -c not set.\n\t"
+		"-m  Service Provider sending BPF map fd.\n\t"
+		"-p  Service Provider sending BPF prog fd.\n\t"
 		"-v  Print context and command information.\n\t"
 		"\nNote: Ensure this boolean command is run when "
 		"testing after a reboot:\n\t"
@@ -67,8 +69,12 @@  static void extract_fd_and_respond(const struct binder_transaction_data *txn_in)
 	}
 
 	if (verbose)
-		printf("Client retrieved Service Providers fd: %d st_dev: %ld\n",
-		       obj->fd, sb.st_dev);
+		printf("Client retrieved %s fd: %d st_dev: %ld\n",
+		       fd_type_str, obj->fd, sb.st_dev);
+
+	/* If testing BPF, then cannot do impersonate check */
+	if (fd_type > BINDER_FD)
+		return;
 
 	memset(&writebuf, 0, sizeof(writebuf));
 	memset(readbuf, 0, sizeof(readbuf));
@@ -141,7 +147,7 @@  static void request_service_provider_fd(int fd, uint32_t handle)
 	writebuf.cmd = BC_TRANSACTION;
 	writebuf.txn.target.handle = handle;
 	writebuf.txn.cookie = 0;
-	writebuf.txn.code = TEST_SERVICE_SEND_CLIENT_SP_FD;
+	writebuf.txn.code = TEST_SERVICE_SEND_FD;
 	writebuf.txn.flags = TF_ACCEPT_FDS;
 
 	writebuf.txn.data_size = 0;
@@ -270,7 +276,7 @@  static int binder_parse(int fd, binder_uintptr_t ptr, binder_size_t size)
 			if (txn->code == TEST_SERVICE_GET)
 				extract_handle_and_acquire(fd, txn);
 
-			if (txn->code == TEST_SERVICE_SEND_CLIENT_SP_FD)
+			if (txn->code == TEST_SERVICE_SEND_FD)
 				extract_fd_and_respond(txn);
 
 			ptr += sizeof(*txn);
@@ -313,8 +319,10 @@  int main(int argc, char **argv)
 	unsigned int readbuf[32];
 
 	transactions_complete = 0;
+	fd_type = BINDER_FD;
+	fd_type_str = "SP";
 
-	while ((opt = getopt(argc, argv, "cnr:v")) != -1) {
+	while ((opt = getopt(argc, argv, "cnr:vmp")) != -1) {
 		switch (opt) {
 		case 'c':
 			use_transactions_complete = true;
@@ -328,6 +336,14 @@  int main(int argc, char **argv)
 		case 'v':
 			verbose = true;
 			break;
+		case 'm':
+			fd_type = BPF_MAP_FD;
+			fd_type_str = "BPF map";
+			break;
+		case 'p':
+			fd_type = BPF_PROG_FD;
+			fd_type_str = "BPF prog";
+			break;
 		default:
 			usage(argv[0]);
 		}
diff --git a/tests/binder/manager.c b/tests/binder/manager.c
index 9922183..8e5f446 100644
--- a/tests/binder/manager.c
+++ b/tests/binder/manager.c
@@ -91,7 +91,7 @@  static void do_service_manager(int fd, struct binder_transaction_data *txn_in)
 		reply_with_handle(fd, txn_in);
 
 		break;
-	case TEST_SERVICE_SEND_CLIENT_SP_FD:
+	case TEST_SERVICE_SEND_FD:
 		if (verbose)
 			printf("Manager Rx'ed SEND_CLIENT_YOUR_BINDER_FD for handle: %d\n",
 			       txn_in->target.handle);
diff --git a/tests/binder/service_provider.c b/tests/binder/service_provider.c
index 2873af8..56d8a43 100644
--- a/tests/binder/service_provider.c
+++ b/tests/binder/service_provider.c
@@ -6,11 +6,14 @@  static int binder_parse(int fd, binder_uintptr_t ptr, binder_size_t size);
 static void usage(char *progname)
 {
 	fprintf(stderr,
-		"usage:  %s [-e expected_ctx] [-f file] [-n] [-v]\n"
+		"usage:  %s -e expected_ctx] [-f file] [-n] [-m|-p|-t] [-v]\n"
 		"Where:\n\t"
 		"-e  Expected security context.\n\t"
 		"-f  Write a line to the file when listening starts.\n\t"
 		"-n  Use the /dev/binderfs name service.\n\t"
+		"-m  Use BPF map fd for transfer.\n\t"
+		"-p  Use BPF prog fd for transfer.\n\t"
+		"-t  Test if BPF enabled.\n\t"
 		"-v  Print context and command information.\n\t"
 		"\nNote: Ensure this boolean command is run when "
 		"testing after a reboot:\n\t"
@@ -34,7 +37,7 @@  static void request_service_provider_fd(int fd,
 	}
 
 	if (verbose)
-		printf("Service Provider sending BC_REPLY with its FD\n");
+		printf("Service Provider sending BC_REPLY with an FD\n");
 
 	memset(writebuf, 0, sizeof(writebuf));
 	memset(&bwr, 0, sizeof(bwr));
@@ -56,17 +59,47 @@  static void request_service_provider_fd(int fd,
 	obj.pad_flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
 #endif
 	obj.cookie = txn->cookie;
-	/* The Service Providers binder fd is used for testing as it allows
+
+	/*
+	 * The Service Providers binder fd is used for testing as it allows
 	 * policy to set whether the Service Provider and Client can be
 	 * allowed access (fd use) or not.
 	 * This also allows a check for the impersonate permission later as
 	 * the Client will use the Service Provider fd to send a transaction.
+	 *
+	 * If a BPF fd is required, it is generated, however it cannot be
+	 * used to check the impersonate permission.
 	 */
-	obj.fd = fd;
+	switch (fd_type) {
+	case BINDER_FD:
+		obj.fd = fd;
+		break;
+#if HAVE_BPF
+	case BPF_MAP_FD:
+		obj.fd = create_bpf_map();
+		if (obj.fd < 0)
+			exit(70);
+		break;
+	case BPF_PROG_FD:
+		obj.fd = create_bpf_prog();
+		if (obj.fd < 0)
+			exit(71);
+		break;
+#else
+	case BPF_MAP_FD:
+	case BPF_PROG_FD:
+		fprintf(stderr, "BPF not supported - Service Provider\n");
+		exit(72);
+		break;
+#endif
+	default:
+		fprintf(stderr, "Invalid fd_type: %d\n", fd_type);
+		exit(73);
+	}
 
 	if (verbose)
-		printf("Service Provider handle: %d and its FD: %d\n",
-		       txn->target.handle, fd);
+		printf("Service Provider handle: %d and %s FD: %d\n",
+		       txn->target.handle, fd_type_str, obj.fd);
 
 	txn->data_size = sizeof(obj);
 	txn->data.ptr.buffer = (binder_uintptr_t)&obj;
@@ -81,7 +114,7 @@  static void request_service_provider_fd(int fd,
 		fprintf(stderr,
 			"Service Provider ioctl BINDER_WRITE_READ error: %s\n",
 			strerror(errno));
-		exit(70);
+		exit(74);
 	}
 }
 
@@ -119,7 +152,7 @@  static int binder_parse(int fd, binder_uintptr_t ptr, binder_size_t size)
 				print_trans_data(txn);
 			}
 
-			if (txn->code == TEST_SERVICE_SEND_CLIENT_SP_FD)
+			if (txn->code == TEST_SERVICE_SEND_FD)
 				request_service_provider_fd(fd, txn);
 
 			ptr += sizeof(*txn);
@@ -148,8 +181,7 @@  static int binder_parse(int fd, binder_uintptr_t ptr, binder_size_t size)
 				}
 			}
 
-			if (txn_ctx->transaction_data.code ==
-			    TEST_SERVICE_SEND_CLIENT_SP_FD)
+			if (txn_ctx->transaction_data.code == TEST_SERVICE_SEND_FD)
 				request_service_provider_fd(fd,
 							    &txn_ctx->transaction_data);
 
@@ -209,8 +241,10 @@  int main(int argc, char **argv)
 	unsigned int readbuf[32];
 
 	expected_ctx = NULL;
+	fd_type = BINDER_FD;
+	fd_type_str = "SP";
 
-	while ((opt = getopt(argc, argv, "e:f:nv")) != -1) {
+	while ((opt = getopt(argc, argv, "e:f:nvmpt")) != -1) {
 		switch (opt) {
 		case 'e':
 			expected_ctx = optarg;
@@ -224,11 +258,37 @@  int main(int argc, char **argv)
 		case 'v':
 			verbose = true;
 			break;
+		case 'm':
+			fd_type = BPF_MAP_FD;
+			fd_type_str = "BPF map";
+			break;
+		case 'p':
+			fd_type = BPF_PROG_FD;
+			fd_type_str = "BPF prog";
+			break;
+		case 't':
+			fd_type = BPF_TEST;
+			break;
 		default:
 			usage(argv[0]);
 		}
 	}
 
+
+#if HAVE_BPF
+	if (fd_type == BPF_TEST)
+		exit(0);
+
+	/* If BPF enabed, then need to set limits */
+	if (fd_type == BPF_MAP_FD || fd_type == BPF_PROG_FD)
+		bpf_setrlimit();
+#else
+	if (fd_type == BPF_TEST) {
+		fprintf(stderr, "BPF not supported\n");
+		exit(-1);
+	}
+#endif
+
 	/* Get our context and pid */
 	result = getcon(&context);
 	if (result < 0) {
@@ -267,19 +327,6 @@  int main(int argc, char **argv)
 		exit(63);
 	}
 
-	if (flag_file) {
-		flag_fd = fopen(flag_file, "w");
-		if (!flag_fd) {
-			fprintf(stderr,
-				"Service Provider failed to open %s: %s\n",
-				flag_file, strerror(errno));
-			result = 64;
-			goto brexit;
-		}
-		fprintf(flag_fd, "listening\n");
-		fclose(flag_fd);
-	}
-
 	memset(&writebuf, 0, sizeof(writebuf));
 	memset(&obj, 0, sizeof(obj));
 	memset(readbuf, 0, sizeof(readbuf));
@@ -322,7 +369,7 @@  int main(int argc, char **argv)
 		fprintf(stderr,
 			"Service Provider ioctl BINDER_WRITE_READ error: %s\n",
 			strerror(errno));
-		result = 65;
+		result = 64;
 		goto brexit;
 	}
 
@@ -338,7 +385,7 @@  int main(int argc, char **argv)
 	    cmd == BR_DEAD_BINDER) {
 		fprintf(stderr, "Service Provider %s() failing command %s, exiting.\n",
 			__func__, cmd_name(cmd));
-		result = 66;
+		result = 65;
 		goto brexit;
 	}
 
@@ -358,10 +405,27 @@  int main(int argc, char **argv)
 		fprintf(stderr,
 			"Service Provider ioctl BINDER_WRITE_READ error: %s\n",
 			strerror(errno));
-		result = 67;
+		result = 66;
 		goto brexit;
 	}
 
+	/*
+	 * Ensure the Manager and Service Provider have completed the
+	 * TEST_SERVICE_ADD sequence before the Client is allowed to start.
+	 */
+	if (flag_file) {
+		flag_fd = fopen(flag_file, "w");
+		if (!flag_fd) {
+			fprintf(stderr,
+				"Service Provider failed to open %s: %s\n",
+				flag_file, strerror(errno));
+			result = 67;
+			goto brexit;
+		}
+		fprintf(flag_fd, "listening\n");
+		fclose(flag_fd);
+	}
+
 	while (true) {
 		memset(readbuf, 0, sizeof(readbuf));
 		bwr.read_size = sizeof(readbuf);
diff --git a/tests/bpf/Makefile b/tests/bpf/Makefile
index 3513179..6fb230d 100644
--- a/tests/bpf/Makefile
+++ b/tests/bpf/Makefile
@@ -5,7 +5,7 @@  LDLIBS += -lselinux -lbpf
 # export so that BPF_ENABLED entries get built correctly on local build
 export CFLAGS += -DHAVE_BPF
 
-BPF_ENABLED = ../fdreceive
+BPF_ENABLED = ../fdreceive ../binder
 
 all: $(TARGETS)
 	@set -e; for i in $(BPF_ENABLED); do $(MAKE) -C $$i all ; done
diff --git a/tests/bpf/test b/tests/bpf/test
index 36f1f32..e284c7f 100755
--- a/tests/bpf/test
+++ b/tests/bpf/test
@@ -4,10 +4,12 @@  use Test::More;
 BEGIN {
     $basedir = $0;
     $basedir =~ s|(.*)/[^/]*|$1|;
-    $fdr_basedir = "$basedir/../fdreceive/";
+    $fdr_basedir    = "$basedir/../fdreceive/";
+    $binder_basedir = "$basedir/../binder/";
 
     $test_count     = 7;
     $test_fdreceive = 0;
+    $test_binder    = 0;
 
     # allow info to be shown during tests
     $v = $ARGV[0];
@@ -28,6 +30,17 @@  BEGIN {
         $test_count += 4;
     }
 
+    # Test if Binder is supported and BPF enabled
+    $result = system("$binder_basedir/check_binder $v 2>/dev/null");
+
+    if ( $result >> 8 eq 0 ) {
+        $result = system("$binder_basedir/service_provider -t 2>/dev/null");
+        if ( $result >> 8 eq 0 ) {
+            $test_binder = 1;
+            $test_count += 4;
+        }
+    }
+
     plan tests => $test_count;
 }
 
@@ -114,4 +127,73 @@  if ($test_fdreceive) {
     system "rm -rf $basedir/test_sock $basedir/flag";
 }
 
+#
+################ BPF Tests for binder #######################
+#
+sub service_start {
+    my ( $service, $runcon_args, $args ) = @_;
+    my $pid;
+    my $flag = $service . "_flag";
+
+    system("mkfifo $basedir/$flag");
+
+    if ( ( $pid = fork() ) == 0 ) {
+        exec
+"runcon $runcon_args $binder_basedir/$service -f $basedir/$flag $args";
+    }
+
+    # Wait for it to initialize.
+    system("read -t 5 <>$basedir/$flag");
+    return $pid;
+}
+
+sub service_end {
+    my ( $service, $pid ) = @_;
+    my $flag = $service . "_flag";
+
+    kill KILL, $pid;
+    waitpid $pid, 0;
+    system("rm -f $basedir/$flag");
+}
+
+if ($test_binder) {
+    ### Test BPF map fd on transfer ##################
+    $sm_pid = service_start( "manager", "-t test_binder_bpf_mgr_t", "$v" );
+    $sp_pid =
+      service_start( "service_provider", "-t test_binder_bpf_provider_t",
+        "-m $v" );
+
+    # Verify that the BPF map fd can be transferred.
+    $result =
+      system
+      "runcon -t test_binder_bpf_client_t $binder_basedir/client $v -m -r 1";
+    ok( $result eq 0 );
+
+    # Verify BPF no map perms.
+    $result = system
+"runcon -t test_binder_client_no_bpf_perm_t $binder_basedir/client $v -m -r 2 2>&1";
+    ok( $result >> 8 eq 141 );
+
+    ### Test BPF prog fd on transfer ##################
+    service_end( "service_provider", $sp_pid );
+    $sp_pid =
+      service_start( "service_provider", "-t test_binder_bpf_provider_t",
+        "-p $v" );
+
+    # Verify that the BPF prog fd can be transferred.
+    $result =
+      system
+      "runcon -t test_binder_bpf_client_t $binder_basedir/client $v -p -r 1";
+    ok( $result eq 0 );
+
+    # Verify BPF no prog perms.
+    $result = system
+"runcon -t test_binder_client_no_bpf_perm_t $binder_basedir/client $v -p -r 2 2>&1";
+    ok( $result >> 8 eq 141 );
+
+    # Kill the service provider & manager.
+    service_end( "service_provider", $sp_pid );
+    service_end( "manager",          $sm_pid );
+}
+
 exit;