diff mbox series

[V4,1/1] selinux-testsuite: Add perf_event tests

Message ID 20191208170646.75714-1-richard_c_haines@btinternet.com (mailing list archive)
State Accepted
Headers show
Series [V4,1/1] selinux-testsuite: Add perf_event tests | expand

Commit Message

Richard Haines Dec. 8, 2019, 5:06 p.m. UTC
Test perf_event permissions.

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
---
V2 Changes:
Remove neverallows from policy
Check /proc/sys/kernel/perf_event_paranoid, if < 2 then bypass the
capability { sys_admin } test.
V3 Changes:
Remove allow_map() and device_t:chr_file rules as not required.
V4 Change:
Correct policy module comment header.

 defconfig                     |   6 ++
 policy/Makefile               |   4 +
 policy/test_perf_event.te     |  89 +++++++++++++++++
 tests/Makefile                |   4 +
 tests/perf_event/.gitignore   |   1 +
 tests/perf_event/Makefile     |   7 ++
 tests/perf_event/perf_event.c | 178 ++++++++++++++++++++++++++++++++++
 tests/perf_event/test         |  93 ++++++++++++++++++
 8 files changed, 382 insertions(+)
 create mode 100644 policy/test_perf_event.te
 create mode 100644 tests/perf_event/.gitignore
 create mode 100644 tests/perf_event/Makefile
 create mode 100644 tests/perf_event/perf_event.c
 create mode 100755 tests/perf_event/test

Comments

Stephen Smalley Dec. 10, 2019, 3:31 p.m. UTC | #1
On 12/8/19 12:06 PM, Richard Haines wrote:
> Test perf_event permissions.
> 
> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>

Acked-by: Stephen Smalley <sds@tycho.nsa.gov>

I'll fix the merge conflict.

> ---
> V2 Changes:
> Remove neverallows from policy
> Check /proc/sys/kernel/perf_event_paranoid, if < 2 then bypass the
> capability { sys_admin } test.
> V3 Changes:
> Remove allow_map() and device_t:chr_file rules as not required.
> V4 Change:
> Correct policy module comment header.
> 
>   defconfig                     |   6 ++
>   policy/Makefile               |   4 +
>   policy/test_perf_event.te     |  89 +++++++++++++++++
>   tests/Makefile                |   4 +
>   tests/perf_event/.gitignore   |   1 +
>   tests/perf_event/Makefile     |   7 ++
>   tests/perf_event/perf_event.c | 178 ++++++++++++++++++++++++++++++++++
>   tests/perf_event/test         |  93 ++++++++++++++++++
>   8 files changed, 382 insertions(+)
>   create mode 100644 policy/test_perf_event.te
>   create mode 100644 tests/perf_event/.gitignore
>   create mode 100644 tests/perf_event/Makefile
>   create mode 100644 tests/perf_event/perf_event.c
>   create mode 100755 tests/perf_event/test
> 
> diff --git a/defconfig b/defconfig
> index 0574f1d..2d9c092 100644
> --- a/defconfig
> +++ b/defconfig
> @@ -78,3 +78,9 @@ CONFIG_KEY_DH_OPERATIONS=y
>   # Test key management socket.
>   # This is not required for SELinux operation itself.
>   CONFIG_NET_KEY=m
> +
> +# Test perf events.
> +# This is not required for SELinux operation itself.
> +CONFIG_HAVE_PERF_EVENTS=y
> +CONFIG_PERF_EVENTS=y
> +CONFIG_TRACEPOINTS=y
> diff --git a/policy/Makefile b/policy/Makefile
> index dda2e16..3f60991 100644
> --- a/policy/Makefile
> +++ b/policy/Makefile
> @@ -101,6 +101,10 @@ ifeq ($(shell grep -q module_load $(POLDEV)/include/support/all_perms.spt && ech
>   TARGETS+=test_module_load.te
>   endif
>   
> +ifeq ($(shell grep -q perf_event $(POLDEV)/include/support/all_perms.spt && echo true),true)
> +TARGETS += test_perf_event.te
> +endif
> +
>   ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6))
>   TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te test_ibpkey.te, $(TARGETS))
>   endif
> diff --git a/policy/test_perf_event.te b/policy/test_perf_event.te
> new file mode 100644
> index 0000000..67250a4
> --- /dev/null
> +++ b/policy/test_perf_event.te
> @@ -0,0 +1,89 @@
> +#
> +######### perf_event policy module ##########
> +#
> +attribute perfdomain;
> +
> +################# Allow perf_event { * } ##########################
> +type test_perf_t;
> +domain_type(test_perf_t)
> +unconfined_runs_test(test_perf_t)
> +typeattribute test_perf_t testdomain;
> +typeattribute test_perf_t perfdomain;
> +
> +allow test_perf_t self:capability { sys_admin };
> +allow test_perf_t self:perf_event { open cpu kernel tracepoint read write };
> +
> +################# Deny capability { sys_admin } ##########################
> +type test_perf_no_admin_t;
> +domain_type(test_perf_no_admin_t)
> +unconfined_runs_test(test_perf_no_admin_t)
> +typeattribute test_perf_no_admin_t testdomain;
> +typeattribute test_perf_no_admin_t perfdomain;
> +
> +allow test_perf_no_admin_t self:perf_event { open cpu kernel tracepoint read write };
> +
> +################# Deny perf_event { open } ##########################
> +type test_perf_no_open_t;
> +domain_type(test_perf_no_open_t)
> +unconfined_runs_test(test_perf_no_open_t)
> +typeattribute test_perf_no_open_t testdomain;
> +typeattribute test_perf_no_open_t perfdomain;
> +
> +allow test_perf_no_open_t self:capability { sys_admin };
> +allow test_perf_no_open_t self:perf_event { cpu kernel tracepoint read write };
> +
> +################# Deny perf_event { cpu } ##########################
> +type test_perf_no_cpu_t;
> +domain_type(test_perf_no_cpu_t)
> +unconfined_runs_test(test_perf_no_cpu_t)
> +typeattribute test_perf_no_cpu_t testdomain;
> +typeattribute test_perf_no_cpu_t perfdomain;
> +
> +allow test_perf_no_cpu_t self:capability { sys_admin };
> +allow test_perf_no_cpu_t self:perf_event { open kernel tracepoint read write };
> +
> +################# Deny perf_event { kernel } ##########################
> +type test_perf_no_kernel_t;
> +domain_type(test_perf_no_kernel_t)
> +unconfined_runs_test(test_perf_no_kernel_t)
> +typeattribute test_perf_no_kernel_t testdomain;
> +typeattribute test_perf_no_kernel_t perfdomain;
> +
> +allow test_perf_no_kernel_t self:capability { sys_admin };
> +allow test_perf_no_kernel_t self:perf_event { open cpu tracepoint read write };
> +
> +################# Deny perf_event { tracepoint } ##########################
> +type test_perf_no_tracepoint_t;
> +domain_type(test_perf_no_tracepoint_t)
> +unconfined_runs_test(test_perf_no_tracepoint_t)
> +typeattribute test_perf_no_tracepoint_t testdomain;
> +typeattribute test_perf_no_tracepoint_t perfdomain;
> +
> +allow test_perf_no_tracepoint_t self:capability { sys_admin };
> +allow test_perf_no_tracepoint_t self:perf_event { open cpu kernel read write };
> +
> +################# Deny perf_event { read } ##########################
> +type test_perf_no_read_t;
> +domain_type(test_perf_no_read_t)
> +unconfined_runs_test(test_perf_no_read_t)
> +typeattribute test_perf_no_read_t testdomain;
> +typeattribute test_perf_no_read_t perfdomain;
> +
> +allow test_perf_no_read_t self:capability { sys_admin };
> +allow test_perf_no_read_t self:perf_event { open cpu kernel tracepoint write };
> +
> +################# Deny perf_event { write } ##########################
> +type test_perf_no_write_t;
> +domain_type(test_perf_no_write_t)
> +unconfined_runs_test(test_perf_no_write_t)
> +typeattribute test_perf_no_write_t testdomain;
> +typeattribute test_perf_no_write_t perfdomain;
> +
> +allow test_perf_no_write_t self:capability { sys_admin };
> +allow test_perf_no_write_t self:perf_event { open cpu kernel tracepoint read };
> +
> +#
> +########### Allow these domains to be entered from sysadm domain ############
> +#
> +miscfiles_domain_entry_test_files(perfdomain)
> +userdom_sysadm_entry_spec_domtrans_to(perfdomain)
> diff --git a/tests/Makefile b/tests/Makefile
> index 0d33fbf..8f66996 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -79,6 +79,10 @@ SUBDIRS+=module_load
>   endif
>   endif
>   
> +ifeq ($(shell grep -q perf_event $(POLDEV)/include/support/all_perms.spt && echo true),true)
> +SUBDIRS += perf_event
> +endif
> +
>   ifeq ($(DISTRO),RHEL4)
>       SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap nnp_nosuid overlay unix_socket, $(SUBDIRS))
>   endif
> diff --git a/tests/perf_event/.gitignore b/tests/perf_event/.gitignore
> new file mode 100644
> index 0000000..8c2f931
> --- /dev/null
> +++ b/tests/perf_event/.gitignore
> @@ -0,0 +1 @@
> +perf_event
> diff --git a/tests/perf_event/Makefile b/tests/perf_event/Makefile
> new file mode 100644
> index 0000000..988424c
> --- /dev/null
> +++ b/tests/perf_event/Makefile
> @@ -0,0 +1,7 @@
> +TARGETS = perf_event
> +LDLIBS += -lselinux
> +
> +all: $(TARGETS)
> +
> +clean:
> +	rm -f $(TARGETS)
> diff --git a/tests/perf_event/perf_event.c b/tests/perf_event/perf_event.c
> new file mode 100644
> index 0000000..8983f02
> --- /dev/null
> +++ b/tests/perf_event/perf_event.c
> @@ -0,0 +1,178 @@
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <stdbool.h>
> +#include <asm/unistd.h>
> +#include <sys/mman.h>
> +#include <sys/ioctl.h>
> +#include <linux/perf_event.h>
> +#include <selinux/selinux.h>
> +
> +enum {
> +	PERF_FILE_MMAP,
> +	PERF_FILE,
> +	PERF_MMAP
> +} read_type;
> +
> +static void print_usage(char *progname)
> +{
> +	fprintf(stderr,
> +		"usage:  %s [-f|-m] [-v]\n"
> +		"Where:\n\t"
> +		"-f  Read perf_event info using read(2)\n\t"
> +		"-m  Read perf_event info using mmap(2)\n\t"
> +		"    Default is to use read(2) and mmap(2)\n\t"
> +		"-v  Print information\n", progname);
> +	exit(-1);
> +}
> +
> +static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
> +			    int cpu, int group_fd, unsigned long flags)
> +{
> +	return syscall(__NR_perf_event_open, hw_event, pid, cpu,
> +		       group_fd, flags);
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	int opt, result, page_size, mmap_size, fd;
> +	long long count;
> +	bool verbose = false;
> +	char *context;
> +	void *base;
> +	struct perf_event_attr pe_attr;
> +	struct perf_event_mmap_page *pe_page;
> +
> +	read_type = PERF_FILE_MMAP;
> +
> +	while ((opt = getopt(argc, argv, "fmv")) != -1) {
> +		switch (opt) {
> +		case 'f':
> +			read_type = PERF_FILE;
> +			break;
> +		case 'm':
> +			read_type = PERF_MMAP;
> +			break;
> +		case 'v':
> +			verbose = true;
> +			break;
> +		default:
> +			print_usage(argv[0]);
> +		}
> +	}
> +
> +	if (verbose) {
> +		result = getcon(&context);
> +		if (result < 0) {
> +			fprintf(stderr, "Failed to obtain process context\n");
> +			exit(-1);
> +		}
> +		printf("Process context:\n\t%s\n", context);
> +		free(context);
> +	}
> +
> +	/* Test perf_event { open cpu kernel tracepoint } */
> +	memset(&pe_attr, 0, sizeof(struct perf_event_attr));
> +	pe_attr.type = PERF_TYPE_HARDWARE | PERF_TYPE_TRACEPOINT;
> +	pe_attr.size = sizeof(struct perf_event_attr);
> +	pe_attr.config = PERF_COUNT_HW_INSTRUCTIONS;
> +	pe_attr.disabled = 1;
> +	pe_attr.exclude_hv = 1;
> +
> +	fd = perf_event_open(&pe_attr, -1, 1, -1, 0);
> +	if (fd < 0) {
> +		fprintf(stderr, "Failed perf_event_open(): %s\n",
> +			strerror(errno));
> +		if (errno == EACCES)
> +			exit(1);
> +		else
> +			exit(-1);
> +	}
> +
> +	/* Test perf_event { write }; */
> +	result = ioctl(fd, PERF_EVENT_IOC_RESET, 0);
> +	if (result < 0) {
> +		fprintf(stderr, "Failed ioctl(PERF_EVENT_IOC_RESET): %s\n",
> +			strerror(errno));
> +		if (errno == EACCES)
> +			result = 2;
> +		goto err;
> +	}
> +
> +	result = ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
> +	if (result < 0) {
> +		fprintf(stderr, "Failed ioctl(PERF_EVENT_IOC_ENABLE): %s\n",
> +			strerror(errno));
> +		if (errno == EACCES)
> +			result = 3;
> +		goto err;
> +	}
> +
> +	result = ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
> +	if (result < 0) {
> +		fprintf(stderr, "Failed ioctl(PERF_EVENT_IOC_DISABLE): %s\n",
> +			strerror(errno));
> +		if (errno == EACCES)
> +			result = 4;
> +		goto err;
> +	}
> +
> +	/* Test mmap(2) perf_event { read } */
> +	if (read_type == PERF_MMAP || read_type == PERF_FILE_MMAP) {
> +		page_size = sysconf(_SC_PAGESIZE);
> +		if (page_size < 0) {
> +			fprintf(stderr, "Failed sysconf(_SC_PAGESIZE): %s\n",
> +				strerror(errno));
> +			if (errno == EACCES)
> +				result = 5;
> +			else
> +				result = -1;
> +			goto err;
> +		}
> +		mmap_size = page_size * 2;
> +
> +		base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
> +			    MAP_SHARED, fd, 0);
> +		if (base == MAP_FAILED) {
> +			fprintf(stderr, "Failed mmap(): %s\n", strerror(errno));
> +			if (errno == EACCES)
> +				result = 6;
> +			else
> +				result = -1;
> +			goto err;
> +		}
> +
> +		if (verbose) {
> +			pe_page = base;
> +			printf("perf mmap(2) return value: %lld\n",
> +			       pe_page->offset);
> +		}
> +
> +		munmap(base, mmap_size);
> +	}
> +
> +	/* Test read(2) perf_event { read } */
> +	if (read_type == PERF_FILE || read_type == PERF_FILE_MMAP) {
> +		result = read(fd, &count, sizeof(long long));
> +		if (result < 0) {
> +			fprintf(stderr, "Failed read(): %s\n", strerror(errno));
> +			if (errno == EACCES)
> +				result = 7;
> +			goto err;
> +		}
> +
> +		if (verbose)
> +			printf("perf read(2) return value: %lld\n", count);
> +
> +		close(fd);
> +	}
> +
> +	return 0;
> +
> +err:
> +	close(fd);
> +	return result;
> +}
> diff --git a/tests/perf_event/test b/tests/perf_event/test
> new file mode 100755
> index 0000000..1c2e4a9
> --- /dev/null
> +++ b/tests/perf_event/test
> @@ -0,0 +1,93 @@
> +#!/usr/bin/perl
> +use Test::More;
> +
> +BEGIN {
> +    $basedir = $0;
> +    $basedir =~ s|(.*)/[^/]*|$1|;
> +
> +    $test_count = 8;
> +    $sys_admin  = 0;
> +
> +    # allow info to be shown during tests
> +    $v = $ARGV[0];
> +    if ($v) {
> +        if ( $v ne "-v" ) {
> +            plan skip_all => "Invalid option (use -v)";
> +        }
> +    }
> +    else {
> +        $v = " ";
> +    }
> +
> +    $level = `cat /proc/sys/kernel/perf_event_paranoid`;
> +    chomp($level);
> +    if ( $level >= 2 ) {    # These tests require CAP_SYS_ADMIN
> +        $test_count += 1;
> +        $sys_admin = 1;
> +    }
> +
> +    if ( $v eq "-v" ) {
> +        print "Paranoid level: $level\n";
> +        if ( $level < 0 ) {
> +            print "\tNot paranoid\n";
> +        }
> +        elsif ( $level eq 0 ) {
> +            print "\tDisallow raw tracepoint/ftrace without CAP_SYS_ADMIN\n";
> +        }
> +        elsif ( $level eq 1 ) {
> +            print "\tDisallow CPU event access without CAP_SYS_ADMIN\n";
> +        }
> +        elsif ( $level eq 2 ) {
> +            print "\tDisallow kernel profiling without CAP_SYS_ADMIN\n";
> +        }
> +        else {
> +            print "\tUndefined level\n";
> +        }
> +    }
> +
> +    plan tests => $test_count;
> +}
> +
> +# perf_event { open cpu kernel tracepoint read write };
> +print "Test perf_event\n";
> +$result = system "runcon -t test_perf_t $basedir/perf_event $v";
> +ok( $result eq 0 );
> +
> +if ($sys_admin) {
> +
> +    # Deny capability { sys_admin } - EACCES perf_event_open(2)
> +    $result =
> +      system "runcon -t test_perf_no_admin_t $basedir/perf_event $v 2>&1";
> +    ok( $result >> 8 eq 1 );
> +}
> +
> +# Deny perf_event { open } - EACCES perf_event_open(2)
> +$result = system "runcon -t test_perf_no_open_t $basedir/perf_event $v 2>&1";
> +ok( $result >> 8 eq 1 );
> +
> +# Deny perf_event { cpu } - EACCES perf_event_open(2)
> +$result = system "runcon -t test_perf_no_cpu_t $basedir/perf_event $v 2>&1";
> +ok( $result >> 8 eq 1 );
> +
> +# Deny perf_event { kernel } - EACCES perf_event_open(2)
> +$result = system "runcon -t test_perf_no_kernel_t $basedir/perf_event $v 2>&1";
> +ok( $result >> 8 eq 1 );
> +
> +# Deny perf_event { tracepoint } - EACCES perf_event_open(2)
> +$result =
> +  system "runcon -t test_perf_no_tracepoint_t $basedir/perf_event $v 2>&1";
> +ok( $result >> 8 eq 1 );
> +
> +# Deny perf_event { read } - EACCES mmap(2)
> +$result = system "runcon -t test_perf_no_read_t $basedir/perf_event -m $v 2>&1";
> +ok( $result >> 8 eq 6 );
> +
> +# Deny perf_event { read } - EACCES read(2)
> +$result = system "runcon -t test_perf_no_read_t $basedir/perf_event -f $v 2>&1";
> +ok( $result >> 8 eq 7 );
> +
> +# Deny perf_event { write } - EACCES ioctl(2) write
> +$result = system "runcon -t test_perf_no_write_t $basedir/perf_event $v 2>&1";
> +ok( $result >> 8 eq 2 );
> +
> +exit;
>
Stephen Smalley Dec. 10, 2019, 5:01 p.m. UTC | #2
On 12/10/19 10:31 AM, Stephen Smalley wrote:
> On 12/8/19 12:06 PM, Richard Haines wrote:
>> Test perf_event permissions.
>>
>> Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
> 
> Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
> 
> I'll fix the merge conflict.

Thanks, merged.

> 
>> ---
>> V2 Changes:
>> Remove neverallows from policy
>> Check /proc/sys/kernel/perf_event_paranoid, if < 2 then bypass the
>> capability { sys_admin } test.
>> V3 Changes:
>> Remove allow_map() and device_t:chr_file rules as not required.
>> V4 Change:
>> Correct policy module comment header.
>>
>>   defconfig                     |   6 ++
>>   policy/Makefile               |   4 +
>>   policy/test_perf_event.te     |  89 +++++++++++++++++
>>   tests/Makefile                |   4 +
>>   tests/perf_event/.gitignore   |   1 +
>>   tests/perf_event/Makefile     |   7 ++
>>   tests/perf_event/perf_event.c | 178 ++++++++++++++++++++++++++++++++++
>>   tests/perf_event/test         |  93 ++++++++++++++++++
>>   8 files changed, 382 insertions(+)
>>   create mode 100644 policy/test_perf_event.te
>>   create mode 100644 tests/perf_event/.gitignore
>>   create mode 100644 tests/perf_event/Makefile
>>   create mode 100644 tests/perf_event/perf_event.c
>>   create mode 100755 tests/perf_event/test
>>
>> diff --git a/defconfig b/defconfig
>> index 0574f1d..2d9c092 100644
>> --- a/defconfig
>> +++ b/defconfig
>> @@ -78,3 +78,9 @@ CONFIG_KEY_DH_OPERATIONS=y
>>   # Test key management socket.
>>   # This is not required for SELinux operation itself.
>>   CONFIG_NET_KEY=m
>> +
>> +# Test perf events.
>> +# This is not required for SELinux operation itself.
>> +CONFIG_HAVE_PERF_EVENTS=y
>> +CONFIG_PERF_EVENTS=y
>> +CONFIG_TRACEPOINTS=y
>> diff --git a/policy/Makefile b/policy/Makefile
>> index dda2e16..3f60991 100644
>> --- a/policy/Makefile
>> +++ b/policy/Makefile
>> @@ -101,6 +101,10 @@ ifeq ($(shell grep -q module_load 
>> $(POLDEV)/include/support/all_perms.spt && ech
>>   TARGETS+=test_module_load.te
>>   endif
>> +ifeq ($(shell grep -q perf_event 
>> $(POLDEV)/include/support/all_perms.spt && echo true),true)
>> +TARGETS += test_perf_event.te
>> +endif
>> +
>>   ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6))
>>   TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te 
>> test_ibpkey.te, $(TARGETS))
>>   endif
>> diff --git a/policy/test_perf_event.te b/policy/test_perf_event.te
>> new file mode 100644
>> index 0000000..67250a4
>> --- /dev/null
>> +++ b/policy/test_perf_event.te
>> @@ -0,0 +1,89 @@
>> +#
>> +######### perf_event policy module ##########
>> +#
>> +attribute perfdomain;
>> +
>> +################# Allow perf_event { * } ##########################
>> +type test_perf_t;
>> +domain_type(test_perf_t)
>> +unconfined_runs_test(test_perf_t)
>> +typeattribute test_perf_t testdomain;
>> +typeattribute test_perf_t perfdomain;
>> +
>> +allow test_perf_t self:capability { sys_admin };
>> +allow test_perf_t self:perf_event { open cpu kernel tracepoint read 
>> write };
>> +
>> +################# Deny capability { sys_admin } 
>> ##########################
>> +type test_perf_no_admin_t;
>> +domain_type(test_perf_no_admin_t)
>> +unconfined_runs_test(test_perf_no_admin_t)
>> +typeattribute test_perf_no_admin_t testdomain;
>> +typeattribute test_perf_no_admin_t perfdomain;
>> +
>> +allow test_perf_no_admin_t self:perf_event { open cpu kernel 
>> tracepoint read write };
>> +
>> +################# Deny perf_event { open } ##########################
>> +type test_perf_no_open_t;
>> +domain_type(test_perf_no_open_t)
>> +unconfined_runs_test(test_perf_no_open_t)
>> +typeattribute test_perf_no_open_t testdomain;
>> +typeattribute test_perf_no_open_t perfdomain;
>> +
>> +allow test_perf_no_open_t self:capability { sys_admin };
>> +allow test_perf_no_open_t self:perf_event { cpu kernel tracepoint 
>> read write };
>> +
>> +################# Deny perf_event { cpu } ##########################
>> +type test_perf_no_cpu_t;
>> +domain_type(test_perf_no_cpu_t)
>> +unconfined_runs_test(test_perf_no_cpu_t)
>> +typeattribute test_perf_no_cpu_t testdomain;
>> +typeattribute test_perf_no_cpu_t perfdomain;
>> +
>> +allow test_perf_no_cpu_t self:capability { sys_admin };
>> +allow test_perf_no_cpu_t self:perf_event { open kernel tracepoint 
>> read write };
>> +
>> +################# Deny perf_event { kernel } ##########################
>> +type test_perf_no_kernel_t;
>> +domain_type(test_perf_no_kernel_t)
>> +unconfined_runs_test(test_perf_no_kernel_t)
>> +typeattribute test_perf_no_kernel_t testdomain;
>> +typeattribute test_perf_no_kernel_t perfdomain;
>> +
>> +allow test_perf_no_kernel_t self:capability { sys_admin };
>> +allow test_perf_no_kernel_t self:perf_event { open cpu tracepoint 
>> read write };
>> +
>> +################# Deny perf_event { tracepoint } 
>> ##########################
>> +type test_perf_no_tracepoint_t;
>> +domain_type(test_perf_no_tracepoint_t)
>> +unconfined_runs_test(test_perf_no_tracepoint_t)
>> +typeattribute test_perf_no_tracepoint_t testdomain;
>> +typeattribute test_perf_no_tracepoint_t perfdomain;
>> +
>> +allow test_perf_no_tracepoint_t self:capability { sys_admin };
>> +allow test_perf_no_tracepoint_t self:perf_event { open cpu kernel 
>> read write };
>> +
>> +################# Deny perf_event { read } ##########################
>> +type test_perf_no_read_t;
>> +domain_type(test_perf_no_read_t)
>> +unconfined_runs_test(test_perf_no_read_t)
>> +typeattribute test_perf_no_read_t testdomain;
>> +typeattribute test_perf_no_read_t perfdomain;
>> +
>> +allow test_perf_no_read_t self:capability { sys_admin };
>> +allow test_perf_no_read_t self:perf_event { open cpu kernel 
>> tracepoint write };
>> +
>> +################# Deny perf_event { write } ##########################
>> +type test_perf_no_write_t;
>> +domain_type(test_perf_no_write_t)
>> +unconfined_runs_test(test_perf_no_write_t)
>> +typeattribute test_perf_no_write_t testdomain;
>> +typeattribute test_perf_no_write_t perfdomain;
>> +
>> +allow test_perf_no_write_t self:capability { sys_admin };
>> +allow test_perf_no_write_t self:perf_event { open cpu kernel 
>> tracepoint read };
>> +
>> +#
>> +########### Allow these domains to be entered from sysadm domain 
>> ############
>> +#
>> +miscfiles_domain_entry_test_files(perfdomain)
>> +userdom_sysadm_entry_spec_domtrans_to(perfdomain)
>> diff --git a/tests/Makefile b/tests/Makefile
>> index 0d33fbf..8f66996 100644
>> --- a/tests/Makefile
>> +++ b/tests/Makefile
>> @@ -79,6 +79,10 @@ SUBDIRS+=module_load
>>   endif
>>   endif
>> +ifeq ($(shell grep -q perf_event 
>> $(POLDEV)/include/support/all_perms.spt && echo true),true)
>> +SUBDIRS += perf_event
>> +endif
>> +
>>   ifeq ($(DISTRO),RHEL4)
>>       SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap 
>> nnp_nosuid overlay unix_socket, $(SUBDIRS))
>>   endif
>> diff --git a/tests/perf_event/.gitignore b/tests/perf_event/.gitignore
>> new file mode 100644
>> index 0000000..8c2f931
>> --- /dev/null
>> +++ b/tests/perf_event/.gitignore
>> @@ -0,0 +1 @@
>> +perf_event
>> diff --git a/tests/perf_event/Makefile b/tests/perf_event/Makefile
>> new file mode 100644
>> index 0000000..988424c
>> --- /dev/null
>> +++ b/tests/perf_event/Makefile
>> @@ -0,0 +1,7 @@
>> +TARGETS = perf_event
>> +LDLIBS += -lselinux
>> +
>> +all: $(TARGETS)
>> +
>> +clean:
>> +    rm -f $(TARGETS)
>> diff --git a/tests/perf_event/perf_event.c 
>> b/tests/perf_event/perf_event.c
>> new file mode 100644
>> index 0000000..8983f02
>> --- /dev/null
>> +++ b/tests/perf_event/perf_event.c
>> @@ -0,0 +1,178 @@
>> +#include <stdlib.h>
>> +#include <stdio.h>
>> +#include <unistd.h>
>> +#include <string.h>
>> +#include <unistd.h>
>> +#include <errno.h>
>> +#include <stdbool.h>
>> +#include <asm/unistd.h>
>> +#include <sys/mman.h>
>> +#include <sys/ioctl.h>
>> +#include <linux/perf_event.h>
>> +#include <selinux/selinux.h>
>> +
>> +enum {
>> +    PERF_FILE_MMAP,
>> +    PERF_FILE,
>> +    PERF_MMAP
>> +} read_type;
>> +
>> +static void print_usage(char *progname)
>> +{
>> +    fprintf(stderr,
>> +        "usage:  %s [-f|-m] [-v]\n"
>> +        "Where:\n\t"
>> +        "-f  Read perf_event info using read(2)\n\t"
>> +        "-m  Read perf_event info using mmap(2)\n\t"
>> +        "    Default is to use read(2) and mmap(2)\n\t"
>> +        "-v  Print information\n", progname);
>> +    exit(-1);
>> +}
>> +
>> +static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
>> +                int cpu, int group_fd, unsigned long flags)
>> +{
>> +    return syscall(__NR_perf_event_open, hw_event, pid, cpu,
>> +               group_fd, flags);
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> +    int opt, result, page_size, mmap_size, fd;
>> +    long long count;
>> +    bool verbose = false;
>> +    char *context;
>> +    void *base;
>> +    struct perf_event_attr pe_attr;
>> +    struct perf_event_mmap_page *pe_page;
>> +
>> +    read_type = PERF_FILE_MMAP;
>> +
>> +    while ((opt = getopt(argc, argv, "fmv")) != -1) {
>> +        switch (opt) {
>> +        case 'f':
>> +            read_type = PERF_FILE;
>> +            break;
>> +        case 'm':
>> +            read_type = PERF_MMAP;
>> +            break;
>> +        case 'v':
>> +            verbose = true;
>> +            break;
>> +        default:
>> +            print_usage(argv[0]);
>> +        }
>> +    }
>> +
>> +    if (verbose) {
>> +        result = getcon(&context);
>> +        if (result < 0) {
>> +            fprintf(stderr, "Failed to obtain process context\n");
>> +            exit(-1);
>> +        }
>> +        printf("Process context:\n\t%s\n", context);
>> +        free(context);
>> +    }
>> +
>> +    /* Test perf_event { open cpu kernel tracepoint } */
>> +    memset(&pe_attr, 0, sizeof(struct perf_event_attr));
>> +    pe_attr.type = PERF_TYPE_HARDWARE | PERF_TYPE_TRACEPOINT;
>> +    pe_attr.size = sizeof(struct perf_event_attr);
>> +    pe_attr.config = PERF_COUNT_HW_INSTRUCTIONS;
>> +    pe_attr.disabled = 1;
>> +    pe_attr.exclude_hv = 1;
>> +
>> +    fd = perf_event_open(&pe_attr, -1, 1, -1, 0);
>> +    if (fd < 0) {
>> +        fprintf(stderr, "Failed perf_event_open(): %s\n",
>> +            strerror(errno));
>> +        if (errno == EACCES)
>> +            exit(1);
>> +        else
>> +            exit(-1);
>> +    }
>> +
>> +    /* Test perf_event { write }; */
>> +    result = ioctl(fd, PERF_EVENT_IOC_RESET, 0);
>> +    if (result < 0) {
>> +        fprintf(stderr, "Failed ioctl(PERF_EVENT_IOC_RESET): %s\n",
>> +            strerror(errno));
>> +        if (errno == EACCES)
>> +            result = 2;
>> +        goto err;
>> +    }
>> +
>> +    result = ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
>> +    if (result < 0) {
>> +        fprintf(stderr, "Failed ioctl(PERF_EVENT_IOC_ENABLE): %s\n",
>> +            strerror(errno));
>> +        if (errno == EACCES)
>> +            result = 3;
>> +        goto err;
>> +    }
>> +
>> +    result = ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
>> +    if (result < 0) {
>> +        fprintf(stderr, "Failed ioctl(PERF_EVENT_IOC_DISABLE): %s\n",
>> +            strerror(errno));
>> +        if (errno == EACCES)
>> +            result = 4;
>> +        goto err;
>> +    }
>> +
>> +    /* Test mmap(2) perf_event { read } */
>> +    if (read_type == PERF_MMAP || read_type == PERF_FILE_MMAP) {
>> +        page_size = sysconf(_SC_PAGESIZE);
>> +        if (page_size < 0) {
>> +            fprintf(stderr, "Failed sysconf(_SC_PAGESIZE): %s\n",
>> +                strerror(errno));
>> +            if (errno == EACCES)
>> +                result = 5;
>> +            else
>> +                result = -1;
>> +            goto err;
>> +        }
>> +        mmap_size = page_size * 2;
>> +
>> +        base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
>> +                MAP_SHARED, fd, 0);
>> +        if (base == MAP_FAILED) {
>> +            fprintf(stderr, "Failed mmap(): %s\n", strerror(errno));
>> +            if (errno == EACCES)
>> +                result = 6;
>> +            else
>> +                result = -1;
>> +            goto err;
>> +        }
>> +
>> +        if (verbose) {
>> +            pe_page = base;
>> +            printf("perf mmap(2) return value: %lld\n",
>> +                   pe_page->offset);
>> +        }
>> +
>> +        munmap(base, mmap_size);
>> +    }
>> +
>> +    /* Test read(2) perf_event { read } */
>> +    if (read_type == PERF_FILE || read_type == PERF_FILE_MMAP) {
>> +        result = read(fd, &count, sizeof(long long));
>> +        if (result < 0) {
>> +            fprintf(stderr, "Failed read(): %s\n", strerror(errno));
>> +            if (errno == EACCES)
>> +                result = 7;
>> +            goto err;
>> +        }
>> +
>> +        if (verbose)
>> +            printf("perf read(2) return value: %lld\n", count);
>> +
>> +        close(fd);
>> +    }
>> +
>> +    return 0;
>> +
>> +err:
>> +    close(fd);
>> +    return result;
>> +}
>> diff --git a/tests/perf_event/test b/tests/perf_event/test
>> new file mode 100755
>> index 0000000..1c2e4a9
>> --- /dev/null
>> +++ b/tests/perf_event/test
>> @@ -0,0 +1,93 @@
>> +#!/usr/bin/perl
>> +use Test::More;
>> +
>> +BEGIN {
>> +    $basedir = $0;
>> +    $basedir =~ s|(.*)/[^/]*|$1|;
>> +
>> +    $test_count = 8;
>> +    $sys_admin  = 0;
>> +
>> +    # allow info to be shown during tests
>> +    $v = $ARGV[0];
>> +    if ($v) {
>> +        if ( $v ne "-v" ) {
>> +            plan skip_all => "Invalid option (use -v)";
>> +        }
>> +    }
>> +    else {
>> +        $v = " ";
>> +    }
>> +
>> +    $level = `cat /proc/sys/kernel/perf_event_paranoid`;
>> +    chomp($level);
>> +    if ( $level >= 2 ) {    # These tests require CAP_SYS_ADMIN
>> +        $test_count += 1;
>> +        $sys_admin = 1;
>> +    }
>> +
>> +    if ( $v eq "-v" ) {
>> +        print "Paranoid level: $level\n";
>> +        if ( $level < 0 ) {
>> +            print "\tNot paranoid\n";
>> +        }
>> +        elsif ( $level eq 0 ) {
>> +            print "\tDisallow raw tracepoint/ftrace without 
>> CAP_SYS_ADMIN\n";
>> +        }
>> +        elsif ( $level eq 1 ) {
>> +            print "\tDisallow CPU event access without CAP_SYS_ADMIN\n";
>> +        }
>> +        elsif ( $level eq 2 ) {
>> +            print "\tDisallow kernel profiling without CAP_SYS_ADMIN\n";
>> +        }
>> +        else {
>> +            print "\tUndefined level\n";
>> +        }
>> +    }
>> +
>> +    plan tests => $test_count;
>> +}
>> +
>> +# perf_event { open cpu kernel tracepoint read write };
>> +print "Test perf_event\n";
>> +$result = system "runcon -t test_perf_t $basedir/perf_event $v";
>> +ok( $result eq 0 );
>> +
>> +if ($sys_admin) {
>> +
>> +    # Deny capability { sys_admin } - EACCES perf_event_open(2)
>> +    $result =
>> +      system "runcon -t test_perf_no_admin_t $basedir/perf_event $v 
>> 2>&1";
>> +    ok( $result >> 8 eq 1 );
>> +}
>> +
>> +# Deny perf_event { open } - EACCES perf_event_open(2)
>> +$result = system "runcon -t test_perf_no_open_t $basedir/perf_event 
>> $v 2>&1";
>> +ok( $result >> 8 eq 1 );
>> +
>> +# Deny perf_event { cpu } - EACCES perf_event_open(2)
>> +$result = system "runcon -t test_perf_no_cpu_t $basedir/perf_event $v 
>> 2>&1";
>> +ok( $result >> 8 eq 1 );
>> +
>> +# Deny perf_event { kernel } - EACCES perf_event_open(2)
>> +$result = system "runcon -t test_perf_no_kernel_t $basedir/perf_event 
>> $v 2>&1";
>> +ok( $result >> 8 eq 1 );
>> +
>> +# Deny perf_event { tracepoint } - EACCES perf_event_open(2)
>> +$result =
>> +  system "runcon -t test_perf_no_tracepoint_t $basedir/perf_event $v 
>> 2>&1";
>> +ok( $result >> 8 eq 1 );
>> +
>> +# Deny perf_event { read } - EACCES mmap(2)
>> +$result = system "runcon -t test_perf_no_read_t $basedir/perf_event 
>> -m $v 2>&1";
>> +ok( $result >> 8 eq 6 );
>> +
>> +# Deny perf_event { read } - EACCES read(2)
>> +$result = system "runcon -t test_perf_no_read_t $basedir/perf_event 
>> -f $v 2>&1";
>> +ok( $result >> 8 eq 7 );
>> +
>> +# Deny perf_event { write } - EACCES ioctl(2) write
>> +$result = system "runcon -t test_perf_no_write_t $basedir/perf_event 
>> $v 2>&1";
>> +ok( $result >> 8 eq 2 );
>> +
>> +exit;
>>
>
diff mbox series

Patch

diff --git a/defconfig b/defconfig
index 0574f1d..2d9c092 100644
--- a/defconfig
+++ b/defconfig
@@ -78,3 +78,9 @@  CONFIG_KEY_DH_OPERATIONS=y
 # Test key management socket.
 # This is not required for SELinux operation itself.
 CONFIG_NET_KEY=m
+
+# Test perf events.
+# This is not required for SELinux operation itself.
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_TRACEPOINTS=y
diff --git a/policy/Makefile b/policy/Makefile
index dda2e16..3f60991 100644
--- a/policy/Makefile
+++ b/policy/Makefile
@@ -101,6 +101,10 @@  ifeq ($(shell grep -q module_load $(POLDEV)/include/support/all_perms.spt && ech
 TARGETS+=test_module_load.te
 endif
 
+ifeq ($(shell grep -q perf_event $(POLDEV)/include/support/all_perms.spt && echo true),true)
+TARGETS += test_perf_event.te
+endif
+
 ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6))
 TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te test_ibpkey.te, $(TARGETS))
 endif
diff --git a/policy/test_perf_event.te b/policy/test_perf_event.te
new file mode 100644
index 0000000..67250a4
--- /dev/null
+++ b/policy/test_perf_event.te
@@ -0,0 +1,89 @@ 
+#
+######### perf_event policy module ##########
+#
+attribute perfdomain;
+
+################# Allow perf_event { * } ##########################
+type test_perf_t;
+domain_type(test_perf_t)
+unconfined_runs_test(test_perf_t)
+typeattribute test_perf_t testdomain;
+typeattribute test_perf_t perfdomain;
+
+allow test_perf_t self:capability { sys_admin };
+allow test_perf_t self:perf_event { open cpu kernel tracepoint read write };
+
+################# Deny capability { sys_admin } ##########################
+type test_perf_no_admin_t;
+domain_type(test_perf_no_admin_t)
+unconfined_runs_test(test_perf_no_admin_t)
+typeattribute test_perf_no_admin_t testdomain;
+typeattribute test_perf_no_admin_t perfdomain;
+
+allow test_perf_no_admin_t self:perf_event { open cpu kernel tracepoint read write };
+
+################# Deny perf_event { open } ##########################
+type test_perf_no_open_t;
+domain_type(test_perf_no_open_t)
+unconfined_runs_test(test_perf_no_open_t)
+typeattribute test_perf_no_open_t testdomain;
+typeattribute test_perf_no_open_t perfdomain;
+
+allow test_perf_no_open_t self:capability { sys_admin };
+allow test_perf_no_open_t self:perf_event { cpu kernel tracepoint read write };
+
+################# Deny perf_event { cpu } ##########################
+type test_perf_no_cpu_t;
+domain_type(test_perf_no_cpu_t)
+unconfined_runs_test(test_perf_no_cpu_t)
+typeattribute test_perf_no_cpu_t testdomain;
+typeattribute test_perf_no_cpu_t perfdomain;
+
+allow test_perf_no_cpu_t self:capability { sys_admin };
+allow test_perf_no_cpu_t self:perf_event { open kernel tracepoint read write };
+
+################# Deny perf_event { kernel } ##########################
+type test_perf_no_kernel_t;
+domain_type(test_perf_no_kernel_t)
+unconfined_runs_test(test_perf_no_kernel_t)
+typeattribute test_perf_no_kernel_t testdomain;
+typeattribute test_perf_no_kernel_t perfdomain;
+
+allow test_perf_no_kernel_t self:capability { sys_admin };
+allow test_perf_no_kernel_t self:perf_event { open cpu tracepoint read write };
+
+################# Deny perf_event { tracepoint } ##########################
+type test_perf_no_tracepoint_t;
+domain_type(test_perf_no_tracepoint_t)
+unconfined_runs_test(test_perf_no_tracepoint_t)
+typeattribute test_perf_no_tracepoint_t testdomain;
+typeattribute test_perf_no_tracepoint_t perfdomain;
+
+allow test_perf_no_tracepoint_t self:capability { sys_admin };
+allow test_perf_no_tracepoint_t self:perf_event { open cpu kernel read write };
+
+################# Deny perf_event { read } ##########################
+type test_perf_no_read_t;
+domain_type(test_perf_no_read_t)
+unconfined_runs_test(test_perf_no_read_t)
+typeattribute test_perf_no_read_t testdomain;
+typeattribute test_perf_no_read_t perfdomain;
+
+allow test_perf_no_read_t self:capability { sys_admin };
+allow test_perf_no_read_t self:perf_event { open cpu kernel tracepoint write };
+
+################# Deny perf_event { write } ##########################
+type test_perf_no_write_t;
+domain_type(test_perf_no_write_t)
+unconfined_runs_test(test_perf_no_write_t)
+typeattribute test_perf_no_write_t testdomain;
+typeattribute test_perf_no_write_t perfdomain;
+
+allow test_perf_no_write_t self:capability { sys_admin };
+allow test_perf_no_write_t self:perf_event { open cpu kernel tracepoint read };
+
+#
+########### Allow these domains to be entered from sysadm domain ############
+#
+miscfiles_domain_entry_test_files(perfdomain)
+userdom_sysadm_entry_spec_domtrans_to(perfdomain)
diff --git a/tests/Makefile b/tests/Makefile
index 0d33fbf..8f66996 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -79,6 +79,10 @@  SUBDIRS+=module_load
 endif
 endif
 
+ifeq ($(shell grep -q perf_event $(POLDEV)/include/support/all_perms.spt && echo true),true)
+SUBDIRS += perf_event
+endif
+
 ifeq ($(DISTRO),RHEL4)
     SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap nnp_nosuid overlay unix_socket, $(SUBDIRS))
 endif
diff --git a/tests/perf_event/.gitignore b/tests/perf_event/.gitignore
new file mode 100644
index 0000000..8c2f931
--- /dev/null
+++ b/tests/perf_event/.gitignore
@@ -0,0 +1 @@ 
+perf_event
diff --git a/tests/perf_event/Makefile b/tests/perf_event/Makefile
new file mode 100644
index 0000000..988424c
--- /dev/null
+++ b/tests/perf_event/Makefile
@@ -0,0 +1,7 @@ 
+TARGETS = perf_event
+LDLIBS += -lselinux
+
+all: $(TARGETS)
+
+clean:
+	rm -f $(TARGETS)
diff --git a/tests/perf_event/perf_event.c b/tests/perf_event/perf_event.c
new file mode 100644
index 0000000..8983f02
--- /dev/null
+++ b/tests/perf_event/perf_event.c
@@ -0,0 +1,178 @@ 
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <asm/unistd.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <linux/perf_event.h>
+#include <selinux/selinux.h>
+
+enum {
+	PERF_FILE_MMAP,
+	PERF_FILE,
+	PERF_MMAP
+} read_type;
+
+static void print_usage(char *progname)
+{
+	fprintf(stderr,
+		"usage:  %s [-f|-m] [-v]\n"
+		"Where:\n\t"
+		"-f  Read perf_event info using read(2)\n\t"
+		"-m  Read perf_event info using mmap(2)\n\t"
+		"    Default is to use read(2) and mmap(2)\n\t"
+		"-v  Print information\n", progname);
+	exit(-1);
+}
+
+static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
+			    int cpu, int group_fd, unsigned long flags)
+{
+	return syscall(__NR_perf_event_open, hw_event, pid, cpu,
+		       group_fd, flags);
+}
+
+int main(int argc, char **argv)
+{
+	int opt, result, page_size, mmap_size, fd;
+	long long count;
+	bool verbose = false;
+	char *context;
+	void *base;
+	struct perf_event_attr pe_attr;
+	struct perf_event_mmap_page *pe_page;
+
+	read_type = PERF_FILE_MMAP;
+
+	while ((opt = getopt(argc, argv, "fmv")) != -1) {
+		switch (opt) {
+		case 'f':
+			read_type = PERF_FILE;
+			break;
+		case 'm':
+			read_type = PERF_MMAP;
+			break;
+		case 'v':
+			verbose = true;
+			break;
+		default:
+			print_usage(argv[0]);
+		}
+	}
+
+	if (verbose) {
+		result = getcon(&context);
+		if (result < 0) {
+			fprintf(stderr, "Failed to obtain process context\n");
+			exit(-1);
+		}
+		printf("Process context:\n\t%s\n", context);
+		free(context);
+	}
+
+	/* Test perf_event { open cpu kernel tracepoint } */
+	memset(&pe_attr, 0, sizeof(struct perf_event_attr));
+	pe_attr.type = PERF_TYPE_HARDWARE | PERF_TYPE_TRACEPOINT;
+	pe_attr.size = sizeof(struct perf_event_attr);
+	pe_attr.config = PERF_COUNT_HW_INSTRUCTIONS;
+	pe_attr.disabled = 1;
+	pe_attr.exclude_hv = 1;
+
+	fd = perf_event_open(&pe_attr, -1, 1, -1, 0);
+	if (fd < 0) {
+		fprintf(stderr, "Failed perf_event_open(): %s\n",
+			strerror(errno));
+		if (errno == EACCES)
+			exit(1);
+		else
+			exit(-1);
+	}
+
+	/* Test perf_event { write }; */
+	result = ioctl(fd, PERF_EVENT_IOC_RESET, 0);
+	if (result < 0) {
+		fprintf(stderr, "Failed ioctl(PERF_EVENT_IOC_RESET): %s\n",
+			strerror(errno));
+		if (errno == EACCES)
+			result = 2;
+		goto err;
+	}
+
+	result = ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
+	if (result < 0) {
+		fprintf(stderr, "Failed ioctl(PERF_EVENT_IOC_ENABLE): %s\n",
+			strerror(errno));
+		if (errno == EACCES)
+			result = 3;
+		goto err;
+	}
+
+	result = ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
+	if (result < 0) {
+		fprintf(stderr, "Failed ioctl(PERF_EVENT_IOC_DISABLE): %s\n",
+			strerror(errno));
+		if (errno == EACCES)
+			result = 4;
+		goto err;
+	}
+
+	/* Test mmap(2) perf_event { read } */
+	if (read_type == PERF_MMAP || read_type == PERF_FILE_MMAP) {
+		page_size = sysconf(_SC_PAGESIZE);
+		if (page_size < 0) {
+			fprintf(stderr, "Failed sysconf(_SC_PAGESIZE): %s\n",
+				strerror(errno));
+			if (errno == EACCES)
+				result = 5;
+			else
+				result = -1;
+			goto err;
+		}
+		mmap_size = page_size * 2;
+
+		base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
+			    MAP_SHARED, fd, 0);
+		if (base == MAP_FAILED) {
+			fprintf(stderr, "Failed mmap(): %s\n", strerror(errno));
+			if (errno == EACCES)
+				result = 6;
+			else
+				result = -1;
+			goto err;
+		}
+
+		if (verbose) {
+			pe_page = base;
+			printf("perf mmap(2) return value: %lld\n",
+			       pe_page->offset);
+		}
+
+		munmap(base, mmap_size);
+	}
+
+	/* Test read(2) perf_event { read } */
+	if (read_type == PERF_FILE || read_type == PERF_FILE_MMAP) {
+		result = read(fd, &count, sizeof(long long));
+		if (result < 0) {
+			fprintf(stderr, "Failed read(): %s\n", strerror(errno));
+			if (errno == EACCES)
+				result = 7;
+			goto err;
+		}
+
+		if (verbose)
+			printf("perf read(2) return value: %lld\n", count);
+
+		close(fd);
+	}
+
+	return 0;
+
+err:
+	close(fd);
+	return result;
+}
diff --git a/tests/perf_event/test b/tests/perf_event/test
new file mode 100755
index 0000000..1c2e4a9
--- /dev/null
+++ b/tests/perf_event/test
@@ -0,0 +1,93 @@ 
+#!/usr/bin/perl
+use Test::More;
+
+BEGIN {
+    $basedir = $0;
+    $basedir =~ s|(.*)/[^/]*|$1|;
+
+    $test_count = 8;
+    $sys_admin  = 0;
+
+    # allow info to be shown during tests
+    $v = $ARGV[0];
+    if ($v) {
+        if ( $v ne "-v" ) {
+            plan skip_all => "Invalid option (use -v)";
+        }
+    }
+    else {
+        $v = " ";
+    }
+
+    $level = `cat /proc/sys/kernel/perf_event_paranoid`;
+    chomp($level);
+    if ( $level >= 2 ) {    # These tests require CAP_SYS_ADMIN
+        $test_count += 1;
+        $sys_admin = 1;
+    }
+
+    if ( $v eq "-v" ) {
+        print "Paranoid level: $level\n";
+        if ( $level < 0 ) {
+            print "\tNot paranoid\n";
+        }
+        elsif ( $level eq 0 ) {
+            print "\tDisallow raw tracepoint/ftrace without CAP_SYS_ADMIN\n";
+        }
+        elsif ( $level eq 1 ) {
+            print "\tDisallow CPU event access without CAP_SYS_ADMIN\n";
+        }
+        elsif ( $level eq 2 ) {
+            print "\tDisallow kernel profiling without CAP_SYS_ADMIN\n";
+        }
+        else {
+            print "\tUndefined level\n";
+        }
+    }
+
+    plan tests => $test_count;
+}
+
+# perf_event { open cpu kernel tracepoint read write };
+print "Test perf_event\n";
+$result = system "runcon -t test_perf_t $basedir/perf_event $v";
+ok( $result eq 0 );
+
+if ($sys_admin) {
+
+    # Deny capability { sys_admin } - EACCES perf_event_open(2)
+    $result =
+      system "runcon -t test_perf_no_admin_t $basedir/perf_event $v 2>&1";
+    ok( $result >> 8 eq 1 );
+}
+
+# Deny perf_event { open } - EACCES perf_event_open(2)
+$result = system "runcon -t test_perf_no_open_t $basedir/perf_event $v 2>&1";
+ok( $result >> 8 eq 1 );
+
+# Deny perf_event { cpu } - EACCES perf_event_open(2)
+$result = system "runcon -t test_perf_no_cpu_t $basedir/perf_event $v 2>&1";
+ok( $result >> 8 eq 1 );
+
+# Deny perf_event { kernel } - EACCES perf_event_open(2)
+$result = system "runcon -t test_perf_no_kernel_t $basedir/perf_event $v 2>&1";
+ok( $result >> 8 eq 1 );
+
+# Deny perf_event { tracepoint } - EACCES perf_event_open(2)
+$result =
+  system "runcon -t test_perf_no_tracepoint_t $basedir/perf_event $v 2>&1";
+ok( $result >> 8 eq 1 );
+
+# Deny perf_event { read } - EACCES mmap(2)
+$result = system "runcon -t test_perf_no_read_t $basedir/perf_event -m $v 2>&1";
+ok( $result >> 8 eq 6 );
+
+# Deny perf_event { read } - EACCES read(2)
+$result = system "runcon -t test_perf_no_read_t $basedir/perf_event -f $v 2>&1";
+ok( $result >> 8 eq 7 );
+
+# Deny perf_event { write } - EACCES ioctl(2) write
+$result = system "runcon -t test_perf_no_write_t $basedir/perf_event $v 2>&1";
+ok( $result >> 8 eq 2 );
+
+exit;