linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 net-next 0/5] eBPF-based device cgroup controller
@ 2017-11-02 17:15 Roman Gushchin
  2017-11-02 17:15 ` [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants Roman Gushchin
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-02 17:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin

This patchset introduces an eBPF-based device controller for cgroup v2.

Patches (1) and (2) are a preparational work required to share some code
  with the existing device controller implementation.
Patch (3) is the main patch, which introduces a new bpf prog type
  and all necessary infrastructure.
Patch (4) moves cgroup_helpers.c/h to use them by patch (4).
Patch (5) implements an example of eBPF program which controls access
  to device files and corresponding userspace test.

v3:
  Renamed constants introduced by patch (3) to BPF_DEVCG_*

v2:
  Added patch (1).

v1:
  https://lkml.org/lkml/2017/11/1/363

Roman Gushchin (5):
  device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants
  device_cgroup: prepare code for bpf-based device controller
  bpf, cgroup: implement eBPF-based device controller for cgroup v2
  bpf: move cgroup_helpers from samples/bpf/ to
    tools/testing/selftesting/bpf/
  selftests/bpf: add a test for device cgroup controller

 include/linux/bpf-cgroup.h                         | 15 ++++
 include/linux/bpf_types.h                          |  3 +
 include/linux/device_cgroup.h                      | 67 +++++++++++++++-
 include/uapi/linux/bpf.h                           | 15 ++++
 kernel/bpf/cgroup.c                                | 67 ++++++++++++++++
 kernel/bpf/syscall.c                               |  7 ++
 kernel/bpf/verifier.c                              |  1 +
 samples/bpf/Makefile                               |  5 +-
 security/device_cgroup.c                           | 91 ++++++---------------
 tools/include/uapi/linux/bpf.h                     | 15 ++++
 tools/testing/selftests/bpf/Makefile               |  6 +-
 .../testing/selftests}/bpf/cgroup_helpers.c        |  0
 .../testing/selftests}/bpf/cgroup_helpers.h        |  0
 tools/testing/selftests/bpf/dev_cgroup.c           | 60 ++++++++++++++
 tools/testing/selftests/bpf/test_dev_cgroup.c      | 93 ++++++++++++++++++++++
 15 files changed, 369 insertions(+), 76 deletions(-)
 rename {samples => tools/testing/selftests}/bpf/cgroup_helpers.c (100%)
 rename {samples => tools/testing/selftests}/bpf/cgroup_helpers.h (100%)
 create mode 100644 tools/testing/selftests/bpf/dev_cgroup.c
 create mode 100644 tools/testing/selftests/bpf/test_dev_cgroup.c

-- 
2.13.6

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants
  2017-11-02 17:15 [PATCH v3 net-next 0/5] eBPF-based device cgroup controller Roman Gushchin
@ 2017-11-02 17:15 ` Roman Gushchin
  2017-11-02 17:54   ` Joe Perches
  2017-11-02 17:15 ` [PATCH v3 net-next 2/5] device_cgroup: prepare code for bpf-based device controller Roman Gushchin
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Roman Gushchin @ 2017-11-02 17:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin, David S . Miller

Rename device type and access type constants defined in
security/device_cgroup.c by adding the DEVCG_ prefix.

The reason behind this renaming is to make them global namespace
friendly, as they will be moved to the corresponding header file
by following patches.

Signed-off-by: Roman Gushchin <guro@fb.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Tejun Heo <tj@kernel.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
 security/device_cgroup.c | 72 ++++++++++++++++++++++++------------------------
 1 file changed, 36 insertions(+), 36 deletions(-)

diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 03c1652c9a1f..76cc0cbbb10d 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -14,14 +14,14 @@
 #include <linux/rcupdate.h>
 #include <linux/mutex.h>
 
-#define ACC_MKNOD 1
-#define ACC_READ  2
-#define ACC_WRITE 4
-#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)
+#define DEVCG_ACC_MKNOD 1
+#define DEVCG_ACC_READ  2
+#define DEVCG_ACC_WRITE 4
+#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE)
 
-#define DEV_BLOCK 1
-#define DEV_CHAR  2
-#define DEV_ALL   4  /* this represents all devices */
+#define DEVCG_DEV_BLOCK 1
+#define DEVCG_DEV_CHAR  2
+#define DEVCG_DEV_ALL   4  /* this represents all devices */
 
 static DEFINE_MUTEX(devcgroup_mutex);
 
@@ -245,21 +245,21 @@ static void set_access(char *acc, short access)
 {
 	int idx = 0;
 	memset(acc, 0, ACCLEN);
-	if (access & ACC_READ)
+	if (access & DEVCG_ACC_READ)
 		acc[idx++] = 'r';
-	if (access & ACC_WRITE)
+	if (access & DEVCG_ACC_WRITE)
 		acc[idx++] = 'w';
-	if (access & ACC_MKNOD)
+	if (access & DEVCG_ACC_MKNOD)
 		acc[idx++] = 'm';
 }
 
 static char type_to_char(short type)
 {
-	if (type == DEV_ALL)
+	if (type == DEVCG_DEV_ALL)
 		return 'a';
-	if (type == DEV_CHAR)
+	if (type == DEVCG_DEV_CHAR)
 		return 'c';
-	if (type == DEV_BLOCK)
+	if (type == DEVCG_DEV_BLOCK)
 		return 'b';
 	return 'X';
 }
@@ -286,10 +286,10 @@ static int devcgroup_seq_show(struct seq_file *m, void *v)
 	 * This way, the file remains as a "whitelist of devices"
 	 */
 	if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
-		set_access(acc, ACC_MASK);
+		set_access(acc, DEVCG_ACC_MASK);
 		set_majmin(maj, ~0);
 		set_majmin(min, ~0);
-		seq_printf(m, "%c %s:%s %s\n", type_to_char(DEV_ALL),
+		seq_printf(m, "%c %s:%s %s\n", type_to_char(DEVCG_DEV_ALL),
 			   maj, min, acc);
 	} else {
 		list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) {
@@ -308,10 +308,10 @@ static int devcgroup_seq_show(struct seq_file *m, void *v)
 /**
  * match_exception	- iterates the exception list trying to find a complete match
  * @exceptions: list of exceptions
- * @type: device type (DEV_BLOCK or DEV_CHAR)
+ * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR)
  * @major: device file major number, ~0 to match all
  * @minor: device file minor number, ~0 to match all
- * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
+ * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD)
  *
  * It is considered a complete match if an exception is found that will
  * contain the entire range of provided parameters.
@@ -324,9 +324,9 @@ static bool match_exception(struct list_head *exceptions, short type,
 	struct dev_exception_item *ex;
 
 	list_for_each_entry_rcu(ex, exceptions, list) {
-		if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
+		if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK))
 			continue;
-		if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
+		if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR))
 			continue;
 		if (ex->major != ~0 && ex->major != major)
 			continue;
@@ -343,10 +343,10 @@ static bool match_exception(struct list_head *exceptions, short type,
 /**
  * match_exception_partial - iterates the exception list trying to find a partial match
  * @exceptions: list of exceptions
- * @type: device type (DEV_BLOCK or DEV_CHAR)
+ * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR)
  * @major: device file major number, ~0 to match all
  * @minor: device file minor number, ~0 to match all
- * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
+ * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD)
  *
  * It is considered a partial match if an exception's range is found to
  * contain *any* of the devices specified by provided parameters. This is
@@ -361,9 +361,9 @@ static bool match_exception_partial(struct list_head *exceptions, short type,
 	struct dev_exception_item *ex;
 
 	list_for_each_entry_rcu(ex, exceptions, list) {
-		if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
+		if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK))
 			continue;
-		if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
+		if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR))
 			continue;
 		/*
 		 * We must be sure that both the exception and the provided
@@ -646,10 +646,10 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
 		}
 		return 0;
 	case 'b':
-		ex.type = DEV_BLOCK;
+		ex.type = DEVCG_DEV_BLOCK;
 		break;
 	case 'c':
-		ex.type = DEV_CHAR;
+		ex.type = DEVCG_DEV_CHAR;
 		break;
 	default:
 		return -EINVAL;
@@ -702,13 +702,13 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
 	for (b++, count = 0; count < 3; count++, b++) {
 		switch (*b) {
 		case 'r':
-			ex.access |= ACC_READ;
+			ex.access |= DEVCG_ACC_READ;
 			break;
 		case 'w':
-			ex.access |= ACC_WRITE;
+			ex.access |= DEVCG_ACC_WRITE;
 			break;
 		case 'm':
-			ex.access |= ACC_MKNOD;
+			ex.access |= DEVCG_ACC_MKNOD;
 			break;
 		case '\n':
 		case '\0':
@@ -805,7 +805,7 @@ struct cgroup_subsys devices_cgrp_subsys = {
  * @type: device type
  * @major: device major number
  * @minor: device minor number
- * @access: combination of ACC_WRITE, ACC_READ and ACC_MKNOD
+ * @access: combination of DEVCG_ACC_WRITE, DEVCG_ACC_READ and DEVCG_ACC_MKNOD
  *
  * returns 0 on success, -EPERM case the operation is not permitted
  */
@@ -838,13 +838,13 @@ int __devcgroup_inode_permission(struct inode *inode, int mask)
 	short type, access = 0;
 
 	if (S_ISBLK(inode->i_mode))
-		type = DEV_BLOCK;
+		type = DEVCG_DEV_BLOCK;
 	if (S_ISCHR(inode->i_mode))
-		type = DEV_CHAR;
+		type = DEVCG_DEV_CHAR;
 	if (mask & MAY_WRITE)
-		access |= ACC_WRITE;
+		access |= DEVCG_ACC_WRITE;
 	if (mask & MAY_READ)
-		access |= ACC_READ;
+		access |= DEVCG_ACC_READ;
 
 	return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
 			access);
@@ -858,11 +858,11 @@ int devcgroup_inode_mknod(int mode, dev_t dev)
 		return 0;
 
 	if (S_ISBLK(mode))
-		type = DEV_BLOCK;
+		type = DEVCG_DEV_BLOCK;
 	else
-		type = DEV_CHAR;
+		type = DEVCG_DEV_CHAR;
 
 	return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
-			ACC_MKNOD);
+			DEVCG_ACC_MKNOD);
 
 }
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 2/5] device_cgroup: prepare code for bpf-based device controller
  2017-11-02 17:15 [PATCH v3 net-next 0/5] eBPF-based device cgroup controller Roman Gushchin
  2017-11-02 17:15 ` [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants Roman Gushchin
@ 2017-11-02 17:15 ` Roman Gushchin
  2017-11-02 17:15 ` [PATCH v3 net-next 3/5] bpf, cgroup: implement eBPF-based device controller for cgroup v2 Roman Gushchin
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-02 17:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin

This is non-functional change to prepare the device cgroup code
for adding eBPF-based controller for cgroups v2.

The patch performs the following changes:
1) __devcgroup_inode_permission() and devcgroup_inode_mknod()
   are moving to the device-cgroup.h and converting into static inline.
2) __devcgroup_check_permission() is exported.
3) devcgroup_check_permission() wrapper is introduced to be used
   by both existing and new bpf-based implementations.

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Tejun Heo <tj@kernel.org>
Acked-by: Alexei Starovoitov <ast@kernel.org>
---
 include/linux/device_cgroup.h | 61 ++++++++++++++++++++++++++++++++++++++++---
 security/device_cgroup.c      | 47 ++-------------------------------
 2 files changed, 59 insertions(+), 49 deletions(-)

diff --git a/include/linux/device_cgroup.h b/include/linux/device_cgroup.h
index 8b64221b432b..1e42d33accbf 100644
--- a/include/linux/device_cgroup.h
+++ b/include/linux/device_cgroup.h
@@ -1,16 +1,69 @@
 #include <linux/fs.h>
 
+#define DEVCG_ACC_MKNOD 1
+#define DEVCG_ACC_READ  2
+#define DEVCG_ACC_WRITE 4
+#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE)
+
+#define DEVCG_DEV_BLOCK 1
+#define DEVCG_DEV_CHAR  2
+#define DEVCG_DEV_ALL   4  /* this represents all devices */
+
+#ifdef CONFIG_CGROUP_DEVICE
+extern int __devcgroup_check_permission(short type, u32 major, u32 minor,
+					short access);
+#else
+static inline int __devcgroup_check_permission(short type, u32 major, u32 minor,
+					       short access)
+{ return 0; }
+#endif
+
 #ifdef CONFIG_CGROUP_DEVICE
-extern int __devcgroup_inode_permission(struct inode *inode, int mask);
-extern int devcgroup_inode_mknod(int mode, dev_t dev);
+static inline int devcgroup_check_permission(short type, u32 major, u32 minor,
+					     short access)
+{
+	return __devcgroup_check_permission(type, major, minor, access);
+}
+
 static inline int devcgroup_inode_permission(struct inode *inode, int mask)
 {
+	short type, access = 0;
+
 	if (likely(!inode->i_rdev))
 		return 0;
-	if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))
+
+	if (S_ISBLK(inode->i_mode))
+		type = DEVCG_DEV_BLOCK;
+	else if (S_ISCHR(inode->i_mode))
+		type = DEVCG_DEV_CHAR;
+	else
 		return 0;
-	return __devcgroup_inode_permission(inode, mask);
+
+	if (mask & MAY_WRITE)
+		access |= DEVCG_ACC_WRITE;
+	if (mask & MAY_READ)
+		access |= DEVCG_ACC_READ;
+
+	return devcgroup_check_permission(type, imajor(inode), iminor(inode),
+					  access);
 }
+
+static inline int devcgroup_inode_mknod(int mode, dev_t dev)
+{
+	short type;
+
+	if (!S_ISBLK(mode) && !S_ISCHR(mode))
+		return 0;
+
+	if (S_ISBLK(mode))
+		type = DEVCG_DEV_BLOCK;
+	else
+		type = DEVCG_DEV_CHAR;
+
+	return devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
+					  DEVCG_ACC_MKNOD);
+}
+
 #else
 static inline int devcgroup_inode_permission(struct inode *inode, int mask)
 { return 0; }
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 76cc0cbbb10d..c54692208dcb 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -14,15 +14,6 @@
 #include <linux/rcupdate.h>
 #include <linux/mutex.h>
 
-#define DEVCG_ACC_MKNOD 1
-#define DEVCG_ACC_READ  2
-#define DEVCG_ACC_WRITE 4
-#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE)
-
-#define DEVCG_DEV_BLOCK 1
-#define DEVCG_DEV_CHAR  2
-#define DEVCG_DEV_ALL   4  /* this represents all devices */
-
 static DEFINE_MUTEX(devcgroup_mutex);
 
 enum devcg_behavior {
@@ -809,8 +800,8 @@ struct cgroup_subsys devices_cgrp_subsys = {
  *
  * returns 0 on success, -EPERM case the operation is not permitted
  */
-static int __devcgroup_check_permission(short type, u32 major, u32 minor,
-				        short access)
+int __devcgroup_check_permission(short type, u32 major, u32 minor,
+				 short access)
 {
 	struct dev_cgroup *dev_cgroup;
 	bool rc;
@@ -832,37 +823,3 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor,
 
 	return 0;
 }
-
-int __devcgroup_inode_permission(struct inode *inode, int mask)
-{
-	short type, access = 0;
-
-	if (S_ISBLK(inode->i_mode))
-		type = DEVCG_DEV_BLOCK;
-	if (S_ISCHR(inode->i_mode))
-		type = DEVCG_DEV_CHAR;
-	if (mask & MAY_WRITE)
-		access |= DEVCG_ACC_WRITE;
-	if (mask & MAY_READ)
-		access |= DEVCG_ACC_READ;
-
-	return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
-			access);
-}
-
-int devcgroup_inode_mknod(int mode, dev_t dev)
-{
-	short type;
-
-	if (!S_ISBLK(mode) && !S_ISCHR(mode))
-		return 0;
-
-	if (S_ISBLK(mode))
-		type = DEVCG_DEV_BLOCK;
-	else
-		type = DEVCG_DEV_CHAR;
-
-	return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
-			DEVCG_ACC_MKNOD);
-
-}
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 3/5] bpf, cgroup: implement eBPF-based device controller for cgroup v2
  2017-11-02 17:15 [PATCH v3 net-next 0/5] eBPF-based device cgroup controller Roman Gushchin
  2017-11-02 17:15 ` [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants Roman Gushchin
  2017-11-02 17:15 ` [PATCH v3 net-next 2/5] device_cgroup: prepare code for bpf-based device controller Roman Gushchin
@ 2017-11-02 17:15 ` Roman Gushchin
  2017-11-02 17:15 ` [PATCH v3 net-next 4/5] bpf: move cgroup_helpers from samples/bpf/ to tools/testing/selftesting/bpf/ Roman Gushchin
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-02 17:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin

Cgroup v2 lacks the device controller, provided by cgroup v1.
This patch adds a new eBPF program type, which in combination
of previously added ability to attach multiple eBPF programs
to a cgroup, will provide a similar functionality, but with some
additional flexibility.

This patch introduces a BPF_PROG_TYPE_CGROUP_DEVICE program type.
A program takes major and minor device numbers, device type
(block/character) and access type (mknod/read/write) as parameters
and returns an integer which defines if the operation should be
allowed or terminated with -EPERM.

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Tejun Heo <tj@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
 include/linux/bpf-cgroup.h     | 15 ++++++++++
 include/linux/bpf_types.h      |  3 ++
 include/linux/device_cgroup.h  |  8 ++++-
 include/uapi/linux/bpf.h       | 15 ++++++++++
 kernel/bpf/cgroup.c            | 67 ++++++++++++++++++++++++++++++++++++++++++
 kernel/bpf/syscall.c           |  7 +++++
 kernel/bpf/verifier.c          |  1 +
 tools/include/uapi/linux/bpf.h | 15 ++++++++++
 8 files changed, 130 insertions(+), 1 deletion(-)

diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h
index 359b6f5d3d90..d77cefb3fe99 100644
--- a/include/linux/bpf-cgroup.h
+++ b/include/linux/bpf-cgroup.h
@@ -66,6 +66,9 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
 				     struct bpf_sock_ops_kern *sock_ops,
 				     enum bpf_attach_type type);
 
+int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
+				      short access, enum bpf_attach_type type);
+
 /* Wrappers for __cgroup_bpf_run_filter_skb() guarded by cgroup_bpf_enabled. */
 #define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb)			      \
 ({									      \
@@ -111,6 +114,17 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
 	}								       \
 	__ret;								       \
 })
+
+#define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access)	      \
+({									      \
+	int __ret = 0;							      \
+	if (cgroup_bpf_enabled)						      \
+		__ret = __cgroup_bpf_check_dev_permission(type, major, minor, \
+							  access,	      \
+							  BPF_CGROUP_DEVICE); \
+									      \
+	__ret;								      \
+})
 #else
 
 struct cgroup_bpf {};
@@ -121,6 +135,7 @@ static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; }
 #define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; })
+#define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; })
 
 #endif /* CONFIG_CGROUP_BPF */
 
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 36418ad43245..963a97ee4b7c 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -18,6 +18,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe)
 BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint)
 BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event)
 #endif
+#ifdef CONFIG_CGROUP_BPF
+BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
+#endif
 
 BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
 BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
diff --git a/include/linux/device_cgroup.h b/include/linux/device_cgroup.h
index 1e42d33accbf..90245a70d940 100644
--- a/include/linux/device_cgroup.h
+++ b/include/linux/device_cgroup.h
@@ -1,4 +1,5 @@
 #include <linux/fs.h>
+#include <linux/bpf-cgroup.h>
 
 #define DEVCG_ACC_MKNOD 1
 #define DEVCG_ACC_READ  2
@@ -18,10 +19,15 @@ static inline int __devcgroup_check_permission(short type, u32 major, u32 minor,
 { return 0; }
 #endif
 
-#ifdef CONFIG_CGROUP_DEVICE
+#if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF)
 static inline int devcgroup_check_permission(short type, u32 major, u32 minor,
 					     short access)
 {
+	int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access);
+
+	if (rc)
+		return -EPERM;
+
 	return __devcgroup_check_permission(type, major, minor, access);
 }
 
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 0b7b54d898bd..ea905863a033 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -131,6 +131,7 @@ enum bpf_prog_type {
 	BPF_PROG_TYPE_LWT_XMIT,
 	BPF_PROG_TYPE_SOCK_OPS,
 	BPF_PROG_TYPE_SK_SKB,
+	BPF_PROG_TYPE_CGROUP_DEVICE,
 };
 
 enum bpf_attach_type {
@@ -140,6 +141,7 @@ enum bpf_attach_type {
 	BPF_CGROUP_SOCK_OPS,
 	BPF_SK_SKB_STREAM_PARSER,
 	BPF_SK_SKB_STREAM_VERDICT,
+	BPF_CGROUP_DEVICE,
 	__MAX_BPF_ATTACH_TYPE
 };
 
@@ -984,4 +986,17 @@ struct bpf_perf_event_value {
 	__u64 running;
 };
 
+#define BPF_DEVCG_ACC_MKNOD	(1ULL << 0)
+#define BPF_DEVCG_ACC_READ	(1ULL << 1)
+#define BPF_DEVCG_ACC_WRITE	(1ULL << 2)
+
+#define BPF_DEVCG_DEV_BLOCK	(1ULL << 0)
+#define BPF_DEVCG_DEV_CHAR	(1ULL << 1)
+
+struct bpf_cgroup_dev_ctx {
+	__u32 access_type; /* (access << 16) | type */
+	__u32 major;
+	__u32 minor;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 3db5a17fcfe8..b789ab78d28f 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -522,3 +522,70 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
 	return ret == 1 ? 0 : -EPERM;
 }
 EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_ops);
+
+int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
+				      short access, enum bpf_attach_type type)
+{
+	struct cgroup *cgrp;
+	struct bpf_cgroup_dev_ctx ctx = {
+		.access_type = (access << 16) | dev_type,
+		.major = major,
+		.minor = minor,
+	};
+	int allow = 1;
+
+	rcu_read_lock();
+	cgrp = task_dfl_cgroup(current);
+	allow = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx,
+				   BPF_PROG_RUN);
+	rcu_read_unlock();
+
+	return !allow;
+}
+EXPORT_SYMBOL(__cgroup_bpf_check_dev_permission);
+
+static const struct bpf_func_proto *
+cgroup_dev_func_proto(enum bpf_func_id func_id)
+{
+	switch (func_id) {
+	case BPF_FUNC_map_lookup_elem:
+		return &bpf_map_lookup_elem_proto;
+	case BPF_FUNC_map_update_elem:
+		return &bpf_map_update_elem_proto;
+	case BPF_FUNC_map_delete_elem:
+		return &bpf_map_delete_elem_proto;
+	case BPF_FUNC_get_current_uid_gid:
+		return &bpf_get_current_uid_gid_proto;
+	case BPF_FUNC_trace_printk:
+		if (capable(CAP_SYS_ADMIN))
+			return bpf_get_trace_printk_proto();
+	default:
+		return NULL;
+	}
+}
+
+static bool cgroup_dev_is_valid_access(int off, int size,
+				       enum bpf_access_type type,
+				       struct bpf_insn_access_aux *info)
+{
+	if (type == BPF_WRITE)
+		return false;
+
+	if (off < 0 || off + size > sizeof(struct bpf_cgroup_dev_ctx))
+		return false;
+	/* The verifier guarantees that size > 0. */
+	if (off % size != 0)
+		return false;
+	if (size != sizeof(__u32))
+		return false;
+
+	return true;
+}
+
+const struct bpf_prog_ops cg_dev_prog_ops = {
+};
+
+const struct bpf_verifier_ops cg_dev_verifier_ops = {
+	.get_func_proto		= cgroup_dev_func_proto,
+	.is_valid_access	= cgroup_dev_is_valid_access,
+};
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 323be2473c4b..08f7b450828e 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -1291,6 +1291,9 @@ static int bpf_prog_attach(const union bpf_attr *attr)
 	case BPF_CGROUP_SOCK_OPS:
 		ptype = BPF_PROG_TYPE_SOCK_OPS;
 		break;
+	case BPF_CGROUP_DEVICE:
+		ptype = BPF_PROG_TYPE_CGROUP_DEVICE;
+		break;
 	case BPF_SK_SKB_STREAM_PARSER:
 	case BPF_SK_SKB_STREAM_VERDICT:
 		return sockmap_get_from_fd(attr, true);
@@ -1343,6 +1346,9 @@ static int bpf_prog_detach(const union bpf_attr *attr)
 	case BPF_CGROUP_SOCK_OPS:
 		ptype = BPF_PROG_TYPE_SOCK_OPS;
 		break;
+	case BPF_CGROUP_DEVICE:
+		ptype = BPF_PROG_TYPE_CGROUP_DEVICE;
+		break;
 	case BPF_SK_SKB_STREAM_PARSER:
 	case BPF_SK_SKB_STREAM_VERDICT:
 		return sockmap_get_from_fd(attr, false);
@@ -1385,6 +1391,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
 	case BPF_CGROUP_INET_EGRESS:
 	case BPF_CGROUP_INET_SOCK_CREATE:
 	case BPF_CGROUP_SOCK_OPS:
+	case BPF_CGROUP_DEVICE:
 		break;
 	default:
 		return -EINVAL;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 2bb6d6aa7085..811f0582a1cc 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3100,6 +3100,7 @@ static int check_return_code(struct bpf_verifier_env *env)
 	case BPF_PROG_TYPE_CGROUP_SKB:
 	case BPF_PROG_TYPE_CGROUP_SOCK:
 	case BPF_PROG_TYPE_SOCK_OPS:
+	case BPF_PROG_TYPE_CGROUP_DEVICE:
 		break;
 	default:
 		return 0;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 4a4b6e78c977..83763a3eb339 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -131,6 +131,7 @@ enum bpf_prog_type {
 	BPF_PROG_TYPE_LWT_XMIT,
 	BPF_PROG_TYPE_SOCK_OPS,
 	BPF_PROG_TYPE_SK_SKB,
+	BPF_PROG_TYPE_CGROUP_DEVICE,
 };
 
 enum bpf_attach_type {
@@ -140,6 +141,7 @@ enum bpf_attach_type {
 	BPF_CGROUP_SOCK_OPS,
 	BPF_SK_SKB_STREAM_PARSER,
 	BPF_SK_SKB_STREAM_VERDICT,
+	BPF_CGROUP_DEVICE,
 	__MAX_BPF_ATTACH_TYPE
 };
 
@@ -984,4 +986,17 @@ struct bpf_perf_event_value {
 	__u64 running;
 };
 
+#define BPF_DEVCG_ACC_MKNOD	(1ULL << 0)
+#define BPF_DEVCG_ACC_READ	(1ULL << 1)
+#define BPF_DEVCG_ACC_WRITE	(1ULL << 2)
+
+#define BPF_DEVCG_DEV_BLOCK	(1ULL << 0)
+#define BPF_DEVCG_DEV_CHAR	(1ULL << 1)
+
+struct bpf_cgroup_dev_ctx {
+	__u32 access_type; /* (access << 16) | type */
+	__u32 major;
+	__u32 minor;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 4/5] bpf: move cgroup_helpers from samples/bpf/ to tools/testing/selftesting/bpf/
  2017-11-02 17:15 [PATCH v3 net-next 0/5] eBPF-based device cgroup controller Roman Gushchin
                   ` (2 preceding siblings ...)
  2017-11-02 17:15 ` [PATCH v3 net-next 3/5] bpf, cgroup: implement eBPF-based device controller for cgroup v2 Roman Gushchin
@ 2017-11-02 17:15 ` Roman Gushchin
  2017-11-02 17:15 ` [PATCH v3 net-next 5/5] selftests/bpf: add a test for device cgroup controller Roman Gushchin
  2017-11-04 13:40 ` [PATCH v3 net-next 0/5] eBPF-based " David Miller
  5 siblings, 0 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-02 17:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin

The purpose of this move is to use these files in bpf tests.

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Tejun Heo <tj@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
 samples/bpf/Makefile                                      | 5 +++--
 tools/testing/selftests/bpf/Makefile                      | 2 +-
 {samples => tools/testing/selftests}/bpf/cgroup_helpers.c | 0
 {samples => tools/testing/selftests}/bpf/cgroup_helpers.h | 0
 4 files changed, 4 insertions(+), 3 deletions(-)
 rename {samples => tools/testing/selftests}/bpf/cgroup_helpers.c (100%)
 rename {samples => tools/testing/selftests}/bpf/cgroup_helpers.h (100%)

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index ea2b9e6135f3..adb1e5dba1ea 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -45,6 +45,7 @@ hostprogs-y += syscall_tp
 
 # Libbpf dependencies
 LIBBPF := ../../tools/lib/bpf/bpf.o
+CGROUP_HELPERS := ../../tools/testing/selftests/bpf/cgroup_helpers.o
 
 test_lru_dist-objs := test_lru_dist.o $(LIBBPF)
 sock_example-objs := sock_example.o $(LIBBPF)
@@ -68,13 +69,13 @@ map_perf_test-objs := bpf_load.o $(LIBBPF) map_perf_test_user.o
 test_overhead-objs := bpf_load.o $(LIBBPF) test_overhead_user.o
 test_cgrp2_array_pin-objs := $(LIBBPF) test_cgrp2_array_pin.o
 test_cgrp2_attach-objs := $(LIBBPF) test_cgrp2_attach.o
-test_cgrp2_attach2-objs := $(LIBBPF) test_cgrp2_attach2.o cgroup_helpers.o
+test_cgrp2_attach2-objs := $(LIBBPF) test_cgrp2_attach2.o $(CGROUP_HELPERS)
 test_cgrp2_sock-objs := $(LIBBPF) test_cgrp2_sock.o
 test_cgrp2_sock2-objs := bpf_load.o $(LIBBPF) test_cgrp2_sock2.o
 xdp1-objs := bpf_load.o $(LIBBPF) xdp1_user.o
 # reuse xdp1 source intentionally
 xdp2-objs := bpf_load.o $(LIBBPF) xdp1_user.o
-test_current_task_under_cgroup-objs := bpf_load.o $(LIBBPF) cgroup_helpers.o \
+test_current_task_under_cgroup-objs := bpf_load.o $(LIBBPF) $(CGROUP_HELPERS) \
 				       test_current_task_under_cgroup_user.o
 trace_event-objs := bpf_load.o $(LIBBPF) trace_event_user.o
 sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 2e7880ea0add..36c34f0218a3 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -22,7 +22,7 @@ TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh
 
 include ../lib.mk
 
-BPFOBJ := $(OUTPUT)/libbpf.a
+BPFOBJ := $(OUTPUT)/libbpf.a $(OUTPUT)/cgroup_helpers.c
 
 $(TEST_GEN_PROGS): $(BPFOBJ)
 
diff --git a/samples/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c
similarity index 100%
rename from samples/bpf/cgroup_helpers.c
rename to tools/testing/selftests/bpf/cgroup_helpers.c
diff --git a/samples/bpf/cgroup_helpers.h b/tools/testing/selftests/bpf/cgroup_helpers.h
similarity index 100%
rename from samples/bpf/cgroup_helpers.h
rename to tools/testing/selftests/bpf/cgroup_helpers.h
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 5/5] selftests/bpf: add a test for device cgroup controller
  2017-11-02 17:15 [PATCH v3 net-next 0/5] eBPF-based device cgroup controller Roman Gushchin
                   ` (3 preceding siblings ...)
  2017-11-02 17:15 ` [PATCH v3 net-next 4/5] bpf: move cgroup_helpers from samples/bpf/ to tools/testing/selftesting/bpf/ Roman Gushchin
@ 2017-11-02 17:15 ` Roman Gushchin
  2017-11-04 13:40 ` [PATCH v3 net-next 0/5] eBPF-based " David Miller
  5 siblings, 0 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-02 17:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin

Add a test for device cgroup controller.

The test loads a simple bpf program which logs all
device access attempts using trace_printk() and forbids
all operations except operations with /dev/zero and
/dev/urandom.

Then the test creates and joins a test cgroup, and attaches
the bpf program to it.

Then it tries to perform some simple device operations
and checks the result:

  create /dev/null (should fail)
  create /dev/zero (should pass)
  copy data from /dev/urandom to /dev/zero (should pass)
  copy data from /dev/urandom to /dev/full (should fail)
  copy data from /dev/random to /dev/zero (should fail)

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Tejun Heo <tj@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
 tools/testing/selftests/bpf/Makefile          |  4 +-
 tools/testing/selftests/bpf/dev_cgroup.c      | 60 +++++++++++++++++
 tools/testing/selftests/bpf/test_dev_cgroup.c | 93 +++++++++++++++++++++++++++
 3 files changed, 155 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/dev_cgroup.c
 create mode 100644 tools/testing/selftests/bpf/test_dev_cgroup.c

diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 36c34f0218a3..64ba3684a4f4 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -12,11 +12,11 @@ CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../i
 LDLIBS += -lcap -lelf
 
 TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
-	test_align test_verifier_log
+	test_align test_verifier_log test_dev_cgroup
 
 TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
 	test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o     \
-	sockmap_verdict_prog.o
+	sockmap_verdict_prog.o dev_cgroup.o
 
 TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh
 
diff --git a/tools/testing/selftests/bpf/dev_cgroup.c b/tools/testing/selftests/bpf/dev_cgroup.c
new file mode 100644
index 000000000000..ce41a3475f27
--- /dev/null
+++ b/tools/testing/selftests/bpf/dev_cgroup.c
@@ -0,0 +1,60 @@
+/* Copyright (c) 2017 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+
+#include <linux/bpf.h>
+#include <linux/version.h>
+#include "bpf_helpers.h"
+
+SEC("cgroup/dev")
+int bpf_prog1(struct bpf_cgroup_dev_ctx *ctx)
+{
+	short type = ctx->access_type & 0xFFFF;
+#ifdef DEBUG
+	short access = ctx->access_type >> 16;
+	char fmt[] = "  %d:%d    \n";
+
+	switch (type) {
+	case BPF_DEVCG_DEV_BLOCK:
+		fmt[0] = 'b';
+		break;
+	case BPF_DEVCG_DEV_CHAR:
+		fmt[0] = 'c';
+		break;
+	default:
+		fmt[0] = '?';
+		break;
+	}
+
+	if (access & BPF_DEVCG_ACC_READ)
+		fmt[8] = 'r';
+
+	if (access & BPF_DEVCG_ACC_WRITE)
+		fmt[9] = 'w';
+
+	if (access & BPF_DEVCG_ACC_MKNOD)
+		fmt[10] = 'm';
+
+	bpf_trace_printk(fmt, sizeof(fmt), ctx->major, ctx->minor);
+#endif
+
+	/* Allow access to /dev/zero and /dev/random.
+	 * Forbid everything else.
+	 */
+	if (ctx->major != 1 || type != BPF_DEVCG_DEV_CHAR)
+		return 0;
+
+	switch (ctx->minor) {
+	case 5: /* 1:5 /dev/zero */
+	case 9: /* 1:9 /dev/urandom */
+		return 1;
+	}
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+__u32 _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/tools/testing/selftests/bpf/test_dev_cgroup.c b/tools/testing/selftests/bpf/test_dev_cgroup.c
new file mode 100644
index 000000000000..02c85d6c89b0
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_dev_cgroup.c
@@ -0,0 +1,93 @@
+/* Copyright (c) 2017 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <linux/bpf.h>
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#include "cgroup_helpers.h"
+
+#define DEV_CGROUP_PROG "./dev_cgroup.o"
+
+#define TEST_CGROUP "test-bpf-based-device-cgroup/"
+
+int main(int argc, char **argv)
+{
+	struct bpf_object *obj;
+	int error = EXIT_FAILURE;
+	int prog_fd, cgroup_fd;
+	__u32 prog_cnt;
+
+	if (bpf_prog_load(DEV_CGROUP_PROG, BPF_PROG_TYPE_CGROUP_DEVICE,
+			  &obj, &prog_fd)) {
+		printf("Failed to load DEV_CGROUP program\n");
+		goto err;
+	}
+
+	if (setup_cgroup_environment()) {
+		printf("Failed to load DEV_CGROUP program\n");
+		goto err;
+	}
+
+	/* Create a cgroup, get fd, and join it */
+	cgroup_fd = create_and_get_cgroup(TEST_CGROUP);
+	if (!cgroup_fd) {
+		printf("Failed to create test cgroup\n");
+		goto err;
+	}
+
+	if (join_cgroup(TEST_CGROUP)) {
+		printf("Failed to join cgroup\n");
+		goto err;
+	}
+
+	/* Attach bpf program */
+	if (bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_DEVICE, 0)) {
+		printf("Failed to attach DEV_CGROUP program");
+		goto err;
+	}
+
+	if (bpf_prog_query(cgroup_fd, BPF_CGROUP_DEVICE, 0, NULL, NULL,
+			   &prog_cnt)) {
+		printf("Failed to query attached programs");
+		goto err;
+	}
+
+	/* All operations with /dev/zero and and /dev/urandom are allowed,
+	 * everything else is forbidden.
+	 */
+	assert(system("rm -f /tmp/test_dev_cgroup_null") == 0);
+	assert(system("mknod /tmp/test_dev_cgroup_null c 1 3"));
+	assert(system("rm -f /tmp/test_dev_cgroup_null") == 0);
+
+	/* /dev/zero is whitelisted */
+	assert(system("rm -f /tmp/test_dev_cgroup_zero") == 0);
+	assert(system("mknod /tmp/test_dev_cgroup_zero c 1 5") == 0);
+	assert(system("rm -f /tmp/test_dev_cgroup_zero") == 0);
+
+	assert(system("dd if=/dev/urandom of=/dev/zero count=64") == 0);
+
+	/* src is allowed, target is forbidden */
+	assert(system("dd if=/dev/urandom of=/dev/full count=64"));
+
+	/* src is forbidden, target is allowed */
+	assert(system("dd if=/dev/random of=/dev/zero count=64"));
+
+	error = 0;
+	printf("test_dev_cgroup:PASS\n");
+
+err:
+	cleanup_cgroup_environment();
+
+	return error;
+}
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants
  2017-11-02 17:15 ` [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants Roman Gushchin
@ 2017-11-02 17:54   ` Joe Perches
  2017-11-02 20:06     ` Roman Gushchin
  0 siblings, 1 reply; 16+ messages in thread
From: Joe Perches @ 2017-11-02 17:54 UTC (permalink / raw)
  To: Roman Gushchin, netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, David S . Miller

On Thu, 2017-11-02 at 13:15 -0400, Roman Gushchin wrote:
> Rename device type and access type constants defined in
> security/device_cgroup.c by adding the DEVCG_ prefix.
> 
> The reason behind this renaming is to make them global namespace
> friendly, as they will be moved to the corresponding header file
> by following patches.
[]
> diff --git a/security/device_cgroup.c b/security/device_cgroup.c
[]
> @@ -14,14 +14,14 @@
>  #include <linux/rcupdate.h>
>  #include <linux/mutex.h>
>  
> -#define ACC_MKNOD 1
> -#define ACC_READ  2
> -#define ACC_WRITE 4
> -#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)
> +#define DEVCG_ACC_MKNOD 1
> +#define DEVCG_ACC_READ  2
> +#define DEVCG_ACC_WRITE 4
> +#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE)

trivia:

major and minor are u32 but all the
type and access uses seem to be "short"

Perhaps u16 (or __u16 if uapi public) instead?

> -#define DEV_BLOCK 1
> -#define DEV_CHAR  2
> -#define DEV_ALL   4  /* this represents all devices */
> +#define DEVCG_DEV_BLOCK 1
> +#define DEVCG_DEV_CHAR  2
> +#define DEVCG_DEV_ALL   4  /* this represents all devices */
>  
>  static DEFINE_MUTEX(devcgroup_mutex);
>  
> @@ -245,21 +245,21 @@ static void set_access(char *acc, short access)
>  {
>  	int idx = 0;
>  	memset(acc, 0, ACCLEN);
> -	if (access & ACC_READ)
> +	if (access & DEVCG_ACC_READ)
>  		acc[idx++] = 'r';
> -	if (access & ACC_WRITE)
> +	if (access & DEVCG_ACC_WRITE)
>  		acc[idx++] = 'w';
> -	if (access & ACC_MKNOD)
> +	if (access & DEVCG_ACC_MKNOD)
>  		acc[idx++] = 'm';
>  }
>  
>  static char type_to_char(short type)
>  {
> -	if (type == DEV_ALL)
> +	if (type == DEVCG_DEV_ALL)
>  		return 'a';
> -	if (type == DEV_CHAR)
> +	if (type == DEVCG_DEV_CHAR)
>  		return 'c';
> -	if (type == DEV_BLOCK)
> +	if (type == DEVCG_DEV_BLOCK)
>  		return 'b';
>  	return 'X';
>  }

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants
  2017-11-02 17:54   ` Joe Perches
@ 2017-11-02 20:06     ` Roman Gushchin
  0 siblings, 0 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-02 20:06 UTC (permalink / raw)
  To: Joe Perches
  Cc: netdev, Tejun Heo, Alexei Starovoitov, Daniel Borkmann,
	linux-kernel, kernel-team, David S . Miller

On Thu, Nov 02, 2017 at 10:54:12AM -0700, Joe Perches wrote:
> On Thu, 2017-11-02 at 13:15 -0400, Roman Gushchin wrote:
> > Rename device type and access type constants defined in
> > security/device_cgroup.c by adding the DEVCG_ prefix.
> > 
> > The reason behind this renaming is to make them global namespace
> > friendly, as they will be moved to the corresponding header file
> > by following patches.
> []
> > diff --git a/security/device_cgroup.c b/security/device_cgroup.c
> []
> > @@ -14,14 +14,14 @@
> >  #include <linux/rcupdate.h>
> >  #include <linux/mutex.h>
> >  
> > -#define ACC_MKNOD 1
> > -#define ACC_READ  2
> > -#define ACC_WRITE 4
> > -#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)
> > +#define DEVCG_ACC_MKNOD 1
> > +#define DEVCG_ACC_READ  2
> > +#define DEVCG_ACC_WRITE 4
> > +#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE)
> 
> trivia:
> 
> major and minor are u32 but all the
> type and access uses seem to be "short"
> 
> Perhaps u16 (or __u16 if uapi public) instead?

It was so for a while, and it doesn't seem to be related with this patchset.
So, I'd prefer to change this in a separate patch.

Thanks!

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 net-next 0/5] eBPF-based device cgroup controller
  2017-11-02 17:15 [PATCH v3 net-next 0/5] eBPF-based device cgroup controller Roman Gushchin
                   ` (4 preceding siblings ...)
  2017-11-02 17:15 ` [PATCH v3 net-next 5/5] selftests/bpf: add a test for device cgroup controller Roman Gushchin
@ 2017-11-04 13:40 ` David Miller
  2017-11-05 13:15   ` Roman Gushchin
  5 siblings, 1 reply; 16+ messages in thread
From: David Miller @ 2017-11-04 13:40 UTC (permalink / raw)
  To: guro; +Cc: netdev, tj, ast, daniel, linux-kernel, kernel-team

From: Roman Gushchin <guro@fb.com>
Date: Thu, 2 Nov 2017 13:15:25 -0400

> This patchset introduces an eBPF-based device controller for cgroup
> v2.

This doesn't apply cleanly to net-next, please respin.

Thank you.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 0/5] eBPF-based device cgroup controller
  2017-11-04 13:40 ` [PATCH v3 net-next 0/5] eBPF-based " David Miller
@ 2017-11-05 13:15   ` Roman Gushchin
  2017-11-05 13:15     ` [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants Roman Gushchin
                       ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-05 13:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin

This patchset introduces an eBPF-based device controller for cgroup v2.

Patches (1) and (2) are a preparational work required to share some code
  with the existing device controller implementation.
Patch (3) is the main patch, which introduces a new bpf prog type
  and all necessary infrastructure.
Patch (4) moves cgroup_helpers.c/h to use them by patch (4).
Patch (5) implements an example of eBPF program which controls access
  to device files and corresponding userspace test.

v3:
  Renamed constants introduced by patch (3) to BPF_DEVCG_*

v2:
  Added patch (1).

v1:
  https://lkml.org/lkml/2017/11/1/363

Roman Gushchin (5):
  device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants
  device_cgroup: prepare code for bpf-based device controller
  bpf, cgroup: implement eBPF-based device controller for cgroup v2
  bpf: move cgroup_helpers from samples/bpf/ to
    tools/testing/selftesting/bpf/
  selftests/bpf: add a test for device cgroup controller

 include/linux/bpf-cgroup.h                         | 15 ++++
 include/linux/bpf_types.h                          |  3 +
 include/linux/device_cgroup.h                      | 67 +++++++++++++++-
 include/uapi/linux/bpf.h                           | 15 ++++
 kernel/bpf/cgroup.c                                | 67 ++++++++++++++++
 kernel/bpf/syscall.c                               |  7 ++
 kernel/bpf/verifier.c                              |  1 +
 samples/bpf/Makefile                               |  5 +-
 security/device_cgroup.c                           | 91 ++++++---------------
 tools/include/uapi/linux/bpf.h                     | 15 ++++
 tools/testing/selftests/bpf/Makefile               |  6 +-
 .../testing/selftests}/bpf/cgroup_helpers.c        |  0
 .../testing/selftests}/bpf/cgroup_helpers.h        |  0
 tools/testing/selftests/bpf/dev_cgroup.c           | 60 ++++++++++++++
 tools/testing/selftests/bpf/test_dev_cgroup.c      | 93 ++++++++++++++++++++++
 15 files changed, 369 insertions(+), 76 deletions(-)
 rename {samples => tools/testing/selftests}/bpf/cgroup_helpers.c (100%)
 rename {samples => tools/testing/selftests}/bpf/cgroup_helpers.h (100%)
 create mode 100644 tools/testing/selftests/bpf/dev_cgroup.c
 create mode 100644 tools/testing/selftests/bpf/test_dev_cgroup.c

-- 
2.13.6

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants
  2017-11-05 13:15   ` Roman Gushchin
@ 2017-11-05 13:15     ` Roman Gushchin
  2017-11-05 13:15     ` [PATCH v3 net-next 2/5] device_cgroup: prepare code for bpf-based device controller Roman Gushchin
                       ` (4 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-05 13:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin, David S . Miller

Rename device type and access type constants defined in
security/device_cgroup.c by adding the DEVCG_ prefix.

The reason behind this renaming is to make them global namespace
friendly, as they will be moved to the corresponding header file
by following patches.

Signed-off-by: Roman Gushchin <guro@fb.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Tejun Heo <tj@kernel.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
 security/device_cgroup.c | 72 ++++++++++++++++++++++++------------------------
 1 file changed, 36 insertions(+), 36 deletions(-)

diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 5ef7e5240563..968c21557ba7 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -15,14 +15,14 @@
 #include <linux/rcupdate.h>
 #include <linux/mutex.h>
 
-#define ACC_MKNOD 1
-#define ACC_READ  2
-#define ACC_WRITE 4
-#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)
+#define DEVCG_ACC_MKNOD 1
+#define DEVCG_ACC_READ  2
+#define DEVCG_ACC_WRITE 4
+#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE)
 
-#define DEV_BLOCK 1
-#define DEV_CHAR  2
-#define DEV_ALL   4  /* this represents all devices */
+#define DEVCG_DEV_BLOCK 1
+#define DEVCG_DEV_CHAR  2
+#define DEVCG_DEV_ALL   4  /* this represents all devices */
 
 static DEFINE_MUTEX(devcgroup_mutex);
 
@@ -246,21 +246,21 @@ static void set_access(char *acc, short access)
 {
 	int idx = 0;
 	memset(acc, 0, ACCLEN);
-	if (access & ACC_READ)
+	if (access & DEVCG_ACC_READ)
 		acc[idx++] = 'r';
-	if (access & ACC_WRITE)
+	if (access & DEVCG_ACC_WRITE)
 		acc[idx++] = 'w';
-	if (access & ACC_MKNOD)
+	if (access & DEVCG_ACC_MKNOD)
 		acc[idx++] = 'm';
 }
 
 static char type_to_char(short type)
 {
-	if (type == DEV_ALL)
+	if (type == DEVCG_DEV_ALL)
 		return 'a';
-	if (type == DEV_CHAR)
+	if (type == DEVCG_DEV_CHAR)
 		return 'c';
-	if (type == DEV_BLOCK)
+	if (type == DEVCG_DEV_BLOCK)
 		return 'b';
 	return 'X';
 }
@@ -287,10 +287,10 @@ static int devcgroup_seq_show(struct seq_file *m, void *v)
 	 * This way, the file remains as a "whitelist of devices"
 	 */
 	if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
-		set_access(acc, ACC_MASK);
+		set_access(acc, DEVCG_ACC_MASK);
 		set_majmin(maj, ~0);
 		set_majmin(min, ~0);
-		seq_printf(m, "%c %s:%s %s\n", type_to_char(DEV_ALL),
+		seq_printf(m, "%c %s:%s %s\n", type_to_char(DEVCG_DEV_ALL),
 			   maj, min, acc);
 	} else {
 		list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) {
@@ -309,10 +309,10 @@ static int devcgroup_seq_show(struct seq_file *m, void *v)
 /**
  * match_exception	- iterates the exception list trying to find a complete match
  * @exceptions: list of exceptions
- * @type: device type (DEV_BLOCK or DEV_CHAR)
+ * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR)
  * @major: device file major number, ~0 to match all
  * @minor: device file minor number, ~0 to match all
- * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
+ * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD)
  *
  * It is considered a complete match if an exception is found that will
  * contain the entire range of provided parameters.
@@ -325,9 +325,9 @@ static bool match_exception(struct list_head *exceptions, short type,
 	struct dev_exception_item *ex;
 
 	list_for_each_entry_rcu(ex, exceptions, list) {
-		if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
+		if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK))
 			continue;
-		if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
+		if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR))
 			continue;
 		if (ex->major != ~0 && ex->major != major)
 			continue;
@@ -344,10 +344,10 @@ static bool match_exception(struct list_head *exceptions, short type,
 /**
  * match_exception_partial - iterates the exception list trying to find a partial match
  * @exceptions: list of exceptions
- * @type: device type (DEV_BLOCK or DEV_CHAR)
+ * @type: device type (DEVCG_DEV_BLOCK or DEVCG_DEV_CHAR)
  * @major: device file major number, ~0 to match all
  * @minor: device file minor number, ~0 to match all
- * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
+ * @access: permission mask (DEVCG_ACC_READ, DEVCG_ACC_WRITE, DEVCG_ACC_MKNOD)
  *
  * It is considered a partial match if an exception's range is found to
  * contain *any* of the devices specified by provided parameters. This is
@@ -362,9 +362,9 @@ static bool match_exception_partial(struct list_head *exceptions, short type,
 	struct dev_exception_item *ex;
 
 	list_for_each_entry_rcu(ex, exceptions, list) {
-		if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
+		if ((type & DEVCG_DEV_BLOCK) && !(ex->type & DEVCG_DEV_BLOCK))
 			continue;
-		if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
+		if ((type & DEVCG_DEV_CHAR) && !(ex->type & DEVCG_DEV_CHAR))
 			continue;
 		/*
 		 * We must be sure that both the exception and the provided
@@ -647,10 +647,10 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
 		}
 		return 0;
 	case 'b':
-		ex.type = DEV_BLOCK;
+		ex.type = DEVCG_DEV_BLOCK;
 		break;
 	case 'c':
-		ex.type = DEV_CHAR;
+		ex.type = DEVCG_DEV_CHAR;
 		break;
 	default:
 		return -EINVAL;
@@ -703,13 +703,13 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
 	for (b++, count = 0; count < 3; count++, b++) {
 		switch (*b) {
 		case 'r':
-			ex.access |= ACC_READ;
+			ex.access |= DEVCG_ACC_READ;
 			break;
 		case 'w':
-			ex.access |= ACC_WRITE;
+			ex.access |= DEVCG_ACC_WRITE;
 			break;
 		case 'm':
-			ex.access |= ACC_MKNOD;
+			ex.access |= DEVCG_ACC_MKNOD;
 			break;
 		case '\n':
 		case '\0':
@@ -806,7 +806,7 @@ struct cgroup_subsys devices_cgrp_subsys = {
  * @type: device type
  * @major: device major number
  * @minor: device minor number
- * @access: combination of ACC_WRITE, ACC_READ and ACC_MKNOD
+ * @access: combination of DEVCG_ACC_WRITE, DEVCG_ACC_READ and DEVCG_ACC_MKNOD
  *
  * returns 0 on success, -EPERM case the operation is not permitted
  */
@@ -839,13 +839,13 @@ int __devcgroup_inode_permission(struct inode *inode, int mask)
 	short type, access = 0;
 
 	if (S_ISBLK(inode->i_mode))
-		type = DEV_BLOCK;
+		type = DEVCG_DEV_BLOCK;
 	if (S_ISCHR(inode->i_mode))
-		type = DEV_CHAR;
+		type = DEVCG_DEV_CHAR;
 	if (mask & MAY_WRITE)
-		access |= ACC_WRITE;
+		access |= DEVCG_ACC_WRITE;
 	if (mask & MAY_READ)
-		access |= ACC_READ;
+		access |= DEVCG_ACC_READ;
 
 	return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
 			access);
@@ -859,11 +859,11 @@ int devcgroup_inode_mknod(int mode, dev_t dev)
 		return 0;
 
 	if (S_ISBLK(mode))
-		type = DEV_BLOCK;
+		type = DEVCG_DEV_BLOCK;
 	else
-		type = DEV_CHAR;
+		type = DEVCG_DEV_CHAR;
 
 	return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
-			ACC_MKNOD);
+			DEVCG_ACC_MKNOD);
 
 }
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 2/5] device_cgroup: prepare code for bpf-based device controller
  2017-11-05 13:15   ` Roman Gushchin
  2017-11-05 13:15     ` [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants Roman Gushchin
@ 2017-11-05 13:15     ` Roman Gushchin
  2017-11-05 13:15     ` [PATCH v3 net-next 3/5] bpf, cgroup: implement eBPF-based device controller for cgroup v2 Roman Gushchin
                       ` (3 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-05 13:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin

This is non-functional change to prepare the device cgroup code
for adding eBPF-based controller for cgroups v2.

The patch performs the following changes:
1) __devcgroup_inode_permission() and devcgroup_inode_mknod()
   are moving to the device-cgroup.h and converting into static inline.
2) __devcgroup_check_permission() is exported.
3) devcgroup_check_permission() wrapper is introduced to be used
   by both existing and new bpf-based implementations.

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Tejun Heo <tj@kernel.org>
Acked-by: Alexei Starovoitov <ast@kernel.org>
---
 include/linux/device_cgroup.h | 61 ++++++++++++++++++++++++++++++++++++++++---
 security/device_cgroup.c      | 47 ++-------------------------------
 2 files changed, 59 insertions(+), 49 deletions(-)

diff --git a/include/linux/device_cgroup.h b/include/linux/device_cgroup.h
index cdbc344a92e4..2d93d7ecd479 100644
--- a/include/linux/device_cgroup.h
+++ b/include/linux/device_cgroup.h
@@ -1,17 +1,70 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #include <linux/fs.h>
 
+#define DEVCG_ACC_MKNOD 1
+#define DEVCG_ACC_READ  2
+#define DEVCG_ACC_WRITE 4
+#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE)
+
+#define DEVCG_DEV_BLOCK 1
+#define DEVCG_DEV_CHAR  2
+#define DEVCG_DEV_ALL   4  /* this represents all devices */
+
+#ifdef CONFIG_CGROUP_DEVICE
+extern int __devcgroup_check_permission(short type, u32 major, u32 minor,
+					short access);
+#else
+static inline int __devcgroup_check_permission(short type, u32 major, u32 minor,
+					       short access)
+{ return 0; }
+#endif
+
 #ifdef CONFIG_CGROUP_DEVICE
-extern int __devcgroup_inode_permission(struct inode *inode, int mask);
-extern int devcgroup_inode_mknod(int mode, dev_t dev);
+static inline int devcgroup_check_permission(short type, u32 major, u32 minor,
+					     short access)
+{
+	return __devcgroup_check_permission(type, major, minor, access);
+}
+
 static inline int devcgroup_inode_permission(struct inode *inode, int mask)
 {
+	short type, access = 0;
+
 	if (likely(!inode->i_rdev))
 		return 0;
-	if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))
+
+	if (S_ISBLK(inode->i_mode))
+		type = DEVCG_DEV_BLOCK;
+	else if (S_ISCHR(inode->i_mode))
+		type = DEVCG_DEV_CHAR;
+	else
 		return 0;
-	return __devcgroup_inode_permission(inode, mask);
+
+	if (mask & MAY_WRITE)
+		access |= DEVCG_ACC_WRITE;
+	if (mask & MAY_READ)
+		access |= DEVCG_ACC_READ;
+
+	return devcgroup_check_permission(type, imajor(inode), iminor(inode),
+					  access);
 }
+
+static inline int devcgroup_inode_mknod(int mode, dev_t dev)
+{
+	short type;
+
+	if (!S_ISBLK(mode) && !S_ISCHR(mode))
+		return 0;
+
+	if (S_ISBLK(mode))
+		type = DEVCG_DEV_BLOCK;
+	else
+		type = DEVCG_DEV_CHAR;
+
+	return devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
+					  DEVCG_ACC_MKNOD);
+}
+
 #else
 static inline int devcgroup_inode_permission(struct inode *inode, int mask)
 { return 0; }
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 968c21557ba7..c65b39bafdfe 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -15,15 +15,6 @@
 #include <linux/rcupdate.h>
 #include <linux/mutex.h>
 
-#define DEVCG_ACC_MKNOD 1
-#define DEVCG_ACC_READ  2
-#define DEVCG_ACC_WRITE 4
-#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE)
-
-#define DEVCG_DEV_BLOCK 1
-#define DEVCG_DEV_CHAR  2
-#define DEVCG_DEV_ALL   4  /* this represents all devices */
-
 static DEFINE_MUTEX(devcgroup_mutex);
 
 enum devcg_behavior {
@@ -810,8 +801,8 @@ struct cgroup_subsys devices_cgrp_subsys = {
  *
  * returns 0 on success, -EPERM case the operation is not permitted
  */
-static int __devcgroup_check_permission(short type, u32 major, u32 minor,
-				        short access)
+int __devcgroup_check_permission(short type, u32 major, u32 minor,
+				 short access)
 {
 	struct dev_cgroup *dev_cgroup;
 	bool rc;
@@ -833,37 +824,3 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor,
 
 	return 0;
 }
-
-int __devcgroup_inode_permission(struct inode *inode, int mask)
-{
-	short type, access = 0;
-
-	if (S_ISBLK(inode->i_mode))
-		type = DEVCG_DEV_BLOCK;
-	if (S_ISCHR(inode->i_mode))
-		type = DEVCG_DEV_CHAR;
-	if (mask & MAY_WRITE)
-		access |= DEVCG_ACC_WRITE;
-	if (mask & MAY_READ)
-		access |= DEVCG_ACC_READ;
-
-	return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
-			access);
-}
-
-int devcgroup_inode_mknod(int mode, dev_t dev)
-{
-	short type;
-
-	if (!S_ISBLK(mode) && !S_ISCHR(mode))
-		return 0;
-
-	if (S_ISBLK(mode))
-		type = DEVCG_DEV_BLOCK;
-	else
-		type = DEVCG_DEV_CHAR;
-
-	return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
-			DEVCG_ACC_MKNOD);
-
-}
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 3/5] bpf, cgroup: implement eBPF-based device controller for cgroup v2
  2017-11-05 13:15   ` Roman Gushchin
  2017-11-05 13:15     ` [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants Roman Gushchin
  2017-11-05 13:15     ` [PATCH v3 net-next 2/5] device_cgroup: prepare code for bpf-based device controller Roman Gushchin
@ 2017-11-05 13:15     ` Roman Gushchin
  2017-11-05 13:15     ` [PATCH v3 net-next 4/5] bpf: move cgroup_helpers from samples/bpf/ to tools/testing/selftesting/bpf/ Roman Gushchin
                       ` (2 subsequent siblings)
  5 siblings, 0 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-05 13:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin

Cgroup v2 lacks the device controller, provided by cgroup v1.
This patch adds a new eBPF program type, which in combination
of previously added ability to attach multiple eBPF programs
to a cgroup, will provide a similar functionality, but with some
additional flexibility.

This patch introduces a BPF_PROG_TYPE_CGROUP_DEVICE program type.
A program takes major and minor device numbers, device type
(block/character) and access type (mknod/read/write) as parameters
and returns an integer which defines if the operation should be
allowed or terminated with -EPERM.

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Tejun Heo <tj@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
 include/linux/bpf-cgroup.h     | 15 ++++++++++
 include/linux/bpf_types.h      |  3 ++
 include/linux/device_cgroup.h  |  8 ++++-
 include/uapi/linux/bpf.h       | 15 ++++++++++
 kernel/bpf/cgroup.c            | 67 ++++++++++++++++++++++++++++++++++++++++++
 kernel/bpf/syscall.c           |  7 +++++
 kernel/bpf/verifier.c          |  1 +
 tools/include/uapi/linux/bpf.h | 15 ++++++++++
 8 files changed, 130 insertions(+), 1 deletion(-)

diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h
index 87a7db9feb38..a7f16e0f8d68 100644
--- a/include/linux/bpf-cgroup.h
+++ b/include/linux/bpf-cgroup.h
@@ -67,6 +67,9 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
 				     struct bpf_sock_ops_kern *sock_ops,
 				     enum bpf_attach_type type);
 
+int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
+				      short access, enum bpf_attach_type type);
+
 /* Wrappers for __cgroup_bpf_run_filter_skb() guarded by cgroup_bpf_enabled. */
 #define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb)			      \
 ({									      \
@@ -112,6 +115,17 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
 	}								       \
 	__ret;								       \
 })
+
+#define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access)	      \
+({									      \
+	int __ret = 0;							      \
+	if (cgroup_bpf_enabled)						      \
+		__ret = __cgroup_bpf_check_dev_permission(type, major, minor, \
+							  access,	      \
+							  BPF_CGROUP_DEVICE); \
+									      \
+	__ret;								      \
+})
 #else
 
 struct cgroup_bpf {};
@@ -122,6 +136,7 @@ static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; }
 #define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; })
+#define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; })
 
 #endif /* CONFIG_CGROUP_BPF */
 
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 53c5b9ad7220..978c1d9c9383 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -19,6 +19,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe)
 BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint)
 BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event)
 #endif
+#ifdef CONFIG_CGROUP_BPF
+BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
+#endif
 
 BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
 BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
diff --git a/include/linux/device_cgroup.h b/include/linux/device_cgroup.h
index 2d93d7ecd479..8557efe096dc 100644
--- a/include/linux/device_cgroup.h
+++ b/include/linux/device_cgroup.h
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #include <linux/fs.h>
+#include <linux/bpf-cgroup.h>
 
 #define DEVCG_ACC_MKNOD 1
 #define DEVCG_ACC_READ  2
@@ -19,10 +20,15 @@ static inline int __devcgroup_check_permission(short type, u32 major, u32 minor,
 { return 0; }
 #endif
 
-#ifdef CONFIG_CGROUP_DEVICE
+#if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF)
 static inline int devcgroup_check_permission(short type, u32 major, u32 minor,
 					     short access)
 {
+	int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access);
+
+	if (rc)
+		return -EPERM;
+
 	return __devcgroup_check_permission(type, major, minor, access);
 }
 
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index a9820677c2ff..d581407bb2dc 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -132,6 +132,7 @@ enum bpf_prog_type {
 	BPF_PROG_TYPE_LWT_XMIT,
 	BPF_PROG_TYPE_SOCK_OPS,
 	BPF_PROG_TYPE_SK_SKB,
+	BPF_PROG_TYPE_CGROUP_DEVICE,
 };
 
 enum bpf_attach_type {
@@ -141,6 +142,7 @@ enum bpf_attach_type {
 	BPF_CGROUP_SOCK_OPS,
 	BPF_SK_SKB_STREAM_PARSER,
 	BPF_SK_SKB_STREAM_VERDICT,
+	BPF_CGROUP_DEVICE,
 	__MAX_BPF_ATTACH_TYPE
 };
 
@@ -984,4 +986,17 @@ struct bpf_perf_event_value {
 	__u64 running;
 };
 
+#define BPF_DEVCG_ACC_MKNOD	(1ULL << 0)
+#define BPF_DEVCG_ACC_READ	(1ULL << 1)
+#define BPF_DEVCG_ACC_WRITE	(1ULL << 2)
+
+#define BPF_DEVCG_DEV_BLOCK	(1ULL << 0)
+#define BPF_DEVCG_DEV_CHAR	(1ULL << 1)
+
+struct bpf_cgroup_dev_ctx {
+	__u32 access_type; /* (access << 16) | type */
+	__u32 major;
+	__u32 minor;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 3db5a17fcfe8..b789ab78d28f 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -522,3 +522,70 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
 	return ret == 1 ? 0 : -EPERM;
 }
 EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_ops);
+
+int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
+				      short access, enum bpf_attach_type type)
+{
+	struct cgroup *cgrp;
+	struct bpf_cgroup_dev_ctx ctx = {
+		.access_type = (access << 16) | dev_type,
+		.major = major,
+		.minor = minor,
+	};
+	int allow = 1;
+
+	rcu_read_lock();
+	cgrp = task_dfl_cgroup(current);
+	allow = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx,
+				   BPF_PROG_RUN);
+	rcu_read_unlock();
+
+	return !allow;
+}
+EXPORT_SYMBOL(__cgroup_bpf_check_dev_permission);
+
+static const struct bpf_func_proto *
+cgroup_dev_func_proto(enum bpf_func_id func_id)
+{
+	switch (func_id) {
+	case BPF_FUNC_map_lookup_elem:
+		return &bpf_map_lookup_elem_proto;
+	case BPF_FUNC_map_update_elem:
+		return &bpf_map_update_elem_proto;
+	case BPF_FUNC_map_delete_elem:
+		return &bpf_map_delete_elem_proto;
+	case BPF_FUNC_get_current_uid_gid:
+		return &bpf_get_current_uid_gid_proto;
+	case BPF_FUNC_trace_printk:
+		if (capable(CAP_SYS_ADMIN))
+			return bpf_get_trace_printk_proto();
+	default:
+		return NULL;
+	}
+}
+
+static bool cgroup_dev_is_valid_access(int off, int size,
+				       enum bpf_access_type type,
+				       struct bpf_insn_access_aux *info)
+{
+	if (type == BPF_WRITE)
+		return false;
+
+	if (off < 0 || off + size > sizeof(struct bpf_cgroup_dev_ctx))
+		return false;
+	/* The verifier guarantees that size > 0. */
+	if (off % size != 0)
+		return false;
+	if (size != sizeof(__u32))
+		return false;
+
+	return true;
+}
+
+const struct bpf_prog_ops cg_dev_prog_ops = {
+};
+
+const struct bpf_verifier_ops cg_dev_verifier_ops = {
+	.get_func_proto		= cgroup_dev_func_proto,
+	.is_valid_access	= cgroup_dev_is_valid_access,
+};
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 323be2473c4b..08f7b450828e 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -1291,6 +1291,9 @@ static int bpf_prog_attach(const union bpf_attr *attr)
 	case BPF_CGROUP_SOCK_OPS:
 		ptype = BPF_PROG_TYPE_SOCK_OPS;
 		break;
+	case BPF_CGROUP_DEVICE:
+		ptype = BPF_PROG_TYPE_CGROUP_DEVICE;
+		break;
 	case BPF_SK_SKB_STREAM_PARSER:
 	case BPF_SK_SKB_STREAM_VERDICT:
 		return sockmap_get_from_fd(attr, true);
@@ -1343,6 +1346,9 @@ static int bpf_prog_detach(const union bpf_attr *attr)
 	case BPF_CGROUP_SOCK_OPS:
 		ptype = BPF_PROG_TYPE_SOCK_OPS;
 		break;
+	case BPF_CGROUP_DEVICE:
+		ptype = BPF_PROG_TYPE_CGROUP_DEVICE;
+		break;
 	case BPF_SK_SKB_STREAM_PARSER:
 	case BPF_SK_SKB_STREAM_VERDICT:
 		return sockmap_get_from_fd(attr, false);
@@ -1385,6 +1391,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
 	case BPF_CGROUP_INET_EGRESS:
 	case BPF_CGROUP_INET_SOCK_CREATE:
 	case BPF_CGROUP_SOCK_OPS:
+	case BPF_CGROUP_DEVICE:
 		break;
 	default:
 		return -EINVAL;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 04357ad5a812..43c13ca3d491 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3127,6 +3127,7 @@ static int check_return_code(struct bpf_verifier_env *env)
 	case BPF_PROG_TYPE_CGROUP_SKB:
 	case BPF_PROG_TYPE_CGROUP_SOCK:
 	case BPF_PROG_TYPE_SOCK_OPS:
+	case BPF_PROG_TYPE_CGROUP_DEVICE:
 		break;
 	default:
 		return 0;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 7cebba491011..33490eb2d22e 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -131,6 +131,7 @@ enum bpf_prog_type {
 	BPF_PROG_TYPE_LWT_XMIT,
 	BPF_PROG_TYPE_SOCK_OPS,
 	BPF_PROG_TYPE_SK_SKB,
+	BPF_PROG_TYPE_CGROUP_DEVICE,
 };
 
 enum bpf_attach_type {
@@ -140,6 +141,7 @@ enum bpf_attach_type {
 	BPF_CGROUP_SOCK_OPS,
 	BPF_SK_SKB_STREAM_PARSER,
 	BPF_SK_SKB_STREAM_VERDICT,
+	BPF_CGROUP_DEVICE,
 	__MAX_BPF_ATTACH_TYPE
 };
 
@@ -983,4 +985,17 @@ struct bpf_perf_event_value {
 	__u64 running;
 };
 
+#define BPF_DEVCG_ACC_MKNOD	(1ULL << 0)
+#define BPF_DEVCG_ACC_READ	(1ULL << 1)
+#define BPF_DEVCG_ACC_WRITE	(1ULL << 2)
+
+#define BPF_DEVCG_DEV_BLOCK	(1ULL << 0)
+#define BPF_DEVCG_DEV_CHAR	(1ULL << 1)
+
+struct bpf_cgroup_dev_ctx {
+	__u32 access_type; /* (access << 16) | type */
+	__u32 major;
+	__u32 minor;
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 4/5] bpf: move cgroup_helpers from samples/bpf/ to tools/testing/selftesting/bpf/
  2017-11-05 13:15   ` Roman Gushchin
                       ` (2 preceding siblings ...)
  2017-11-05 13:15     ` [PATCH v3 net-next 3/5] bpf, cgroup: implement eBPF-based device controller for cgroup v2 Roman Gushchin
@ 2017-11-05 13:15     ` Roman Gushchin
  2017-11-05 13:15     ` [PATCH v3 net-next 5/5] selftests/bpf: add a test for device cgroup controller Roman Gushchin
  2017-11-05 14:27     ` [PATCH v3 net-next 0/5] eBPF-based " David Miller
  5 siblings, 0 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-05 13:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin

The purpose of this move is to use these files in bpf tests.

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Tejun Heo <tj@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
 samples/bpf/Makefile                                      | 5 +++--
 tools/testing/selftests/bpf/Makefile                      | 2 +-
 {samples => tools/testing/selftests}/bpf/cgroup_helpers.c | 0
 {samples => tools/testing/selftests}/bpf/cgroup_helpers.h | 0
 4 files changed, 4 insertions(+), 3 deletions(-)
 rename {samples => tools/testing/selftests}/bpf/cgroup_helpers.c (100%)
 rename {samples => tools/testing/selftests}/bpf/cgroup_helpers.h (100%)

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 6a9321ec348a..5994075b080d 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -46,6 +46,7 @@ hostprogs-y += syscall_tp
 
 # Libbpf dependencies
 LIBBPF := ../../tools/lib/bpf/bpf.o
+CGROUP_HELPERS := ../../tools/testing/selftests/bpf/cgroup_helpers.o
 
 test_lru_dist-objs := test_lru_dist.o $(LIBBPF)
 sock_example-objs := sock_example.o $(LIBBPF)
@@ -69,13 +70,13 @@ map_perf_test-objs := bpf_load.o $(LIBBPF) map_perf_test_user.o
 test_overhead-objs := bpf_load.o $(LIBBPF) test_overhead_user.o
 test_cgrp2_array_pin-objs := $(LIBBPF) test_cgrp2_array_pin.o
 test_cgrp2_attach-objs := $(LIBBPF) test_cgrp2_attach.o
-test_cgrp2_attach2-objs := $(LIBBPF) test_cgrp2_attach2.o cgroup_helpers.o
+test_cgrp2_attach2-objs := $(LIBBPF) test_cgrp2_attach2.o $(CGROUP_HELPERS)
 test_cgrp2_sock-objs := $(LIBBPF) test_cgrp2_sock.o
 test_cgrp2_sock2-objs := bpf_load.o $(LIBBPF) test_cgrp2_sock2.o
 xdp1-objs := bpf_load.o $(LIBBPF) xdp1_user.o
 # reuse xdp1 source intentionally
 xdp2-objs := bpf_load.o $(LIBBPF) xdp1_user.o
-test_current_task_under_cgroup-objs := bpf_load.o $(LIBBPF) cgroup_helpers.o \
+test_current_task_under_cgroup-objs := bpf_load.o $(LIBBPF) $(CGROUP_HELPERS) \
 				       test_current_task_under_cgroup_user.o
 trace_event-objs := bpf_load.o $(LIBBPF) trace_event_user.o
 sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 4f0734aa6e93..9fbb02638198 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -23,7 +23,7 @@ TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh
 
 include ../lib.mk
 
-BPFOBJ := $(OUTPUT)/libbpf.a
+BPFOBJ := $(OUTPUT)/libbpf.a $(OUTPUT)/cgroup_helpers.c
 
 $(TEST_GEN_PROGS): $(BPFOBJ)
 
diff --git a/samples/bpf/cgroup_helpers.c b/tools/testing/selftests/bpf/cgroup_helpers.c
similarity index 100%
rename from samples/bpf/cgroup_helpers.c
rename to tools/testing/selftests/bpf/cgroup_helpers.c
diff --git a/samples/bpf/cgroup_helpers.h b/tools/testing/selftests/bpf/cgroup_helpers.h
similarity index 100%
rename from samples/bpf/cgroup_helpers.h
rename to tools/testing/selftests/bpf/cgroup_helpers.h
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 net-next 5/5] selftests/bpf: add a test for device cgroup controller
  2017-11-05 13:15   ` Roman Gushchin
                       ` (3 preceding siblings ...)
  2017-11-05 13:15     ` [PATCH v3 net-next 4/5] bpf: move cgroup_helpers from samples/bpf/ to tools/testing/selftesting/bpf/ Roman Gushchin
@ 2017-11-05 13:15     ` Roman Gushchin
  2017-11-05 14:27     ` [PATCH v3 net-next 0/5] eBPF-based " David Miller
  5 siblings, 0 replies; 16+ messages in thread
From: Roman Gushchin @ 2017-11-05 13:15 UTC (permalink / raw)
  To: netdev
  Cc: Tejun Heo, Alexei Starovoitov, Daniel Borkmann, linux-kernel,
	kernel-team, Roman Gushchin

Add a test for device cgroup controller.

The test loads a simple bpf program which logs all
device access attempts using trace_printk() and forbids
all operations except operations with /dev/zero and
/dev/urandom.

Then the test creates and joins a test cgroup, and attaches
the bpf program to it.

Then it tries to perform some simple device operations
and checks the result:

  create /dev/null (should fail)
  create /dev/zero (should pass)
  copy data from /dev/urandom to /dev/zero (should pass)
  copy data from /dev/urandom to /dev/full (should fail)
  copy data from /dev/random to /dev/zero (should fail)

Signed-off-by: Roman Gushchin <guro@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Tejun Heo <tj@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
---
 tools/testing/selftests/bpf/Makefile          |  4 +-
 tools/testing/selftests/bpf/dev_cgroup.c      | 60 +++++++++++++++++
 tools/testing/selftests/bpf/test_dev_cgroup.c | 93 +++++++++++++++++++++++++++
 3 files changed, 155 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/dev_cgroup.c
 create mode 100644 tools/testing/selftests/bpf/test_dev_cgroup.c

diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 9fbb02638198..333a48655ee0 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -13,11 +13,11 @@ CFLAGS += -Wall -O2 -I$(APIDIR) -I$(LIBDIR) -I$(GENDIR) $(GENFLAGS) -I../../../i
 LDLIBS += -lcap -lelf
 
 TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
-	test_align test_verifier_log
+	test_align test_verifier_log test_dev_cgroup
 
 TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
 	test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o     \
-	sockmap_verdict_prog.o
+	sockmap_verdict_prog.o dev_cgroup.o
 
 TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh
 
diff --git a/tools/testing/selftests/bpf/dev_cgroup.c b/tools/testing/selftests/bpf/dev_cgroup.c
new file mode 100644
index 000000000000..ce41a3475f27
--- /dev/null
+++ b/tools/testing/selftests/bpf/dev_cgroup.c
@@ -0,0 +1,60 @@
+/* Copyright (c) 2017 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+
+#include <linux/bpf.h>
+#include <linux/version.h>
+#include "bpf_helpers.h"
+
+SEC("cgroup/dev")
+int bpf_prog1(struct bpf_cgroup_dev_ctx *ctx)
+{
+	short type = ctx->access_type & 0xFFFF;
+#ifdef DEBUG
+	short access = ctx->access_type >> 16;
+	char fmt[] = "  %d:%d    \n";
+
+	switch (type) {
+	case BPF_DEVCG_DEV_BLOCK:
+		fmt[0] = 'b';
+		break;
+	case BPF_DEVCG_DEV_CHAR:
+		fmt[0] = 'c';
+		break;
+	default:
+		fmt[0] = '?';
+		break;
+	}
+
+	if (access & BPF_DEVCG_ACC_READ)
+		fmt[8] = 'r';
+
+	if (access & BPF_DEVCG_ACC_WRITE)
+		fmt[9] = 'w';
+
+	if (access & BPF_DEVCG_ACC_MKNOD)
+		fmt[10] = 'm';
+
+	bpf_trace_printk(fmt, sizeof(fmt), ctx->major, ctx->minor);
+#endif
+
+	/* Allow access to /dev/zero and /dev/random.
+	 * Forbid everything else.
+	 */
+	if (ctx->major != 1 || type != BPF_DEVCG_DEV_CHAR)
+		return 0;
+
+	switch (ctx->minor) {
+	case 5: /* 1:5 /dev/zero */
+	case 9: /* 1:9 /dev/urandom */
+		return 1;
+	}
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+__u32 _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/tools/testing/selftests/bpf/test_dev_cgroup.c b/tools/testing/selftests/bpf/test_dev_cgroup.c
new file mode 100644
index 000000000000..02c85d6c89b0
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_dev_cgroup.c
@@ -0,0 +1,93 @@
+/* Copyright (c) 2017 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <linux/bpf.h>
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#include "cgroup_helpers.h"
+
+#define DEV_CGROUP_PROG "./dev_cgroup.o"
+
+#define TEST_CGROUP "test-bpf-based-device-cgroup/"
+
+int main(int argc, char **argv)
+{
+	struct bpf_object *obj;
+	int error = EXIT_FAILURE;
+	int prog_fd, cgroup_fd;
+	__u32 prog_cnt;
+
+	if (bpf_prog_load(DEV_CGROUP_PROG, BPF_PROG_TYPE_CGROUP_DEVICE,
+			  &obj, &prog_fd)) {
+		printf("Failed to load DEV_CGROUP program\n");
+		goto err;
+	}
+
+	if (setup_cgroup_environment()) {
+		printf("Failed to load DEV_CGROUP program\n");
+		goto err;
+	}
+
+	/* Create a cgroup, get fd, and join it */
+	cgroup_fd = create_and_get_cgroup(TEST_CGROUP);
+	if (!cgroup_fd) {
+		printf("Failed to create test cgroup\n");
+		goto err;
+	}
+
+	if (join_cgroup(TEST_CGROUP)) {
+		printf("Failed to join cgroup\n");
+		goto err;
+	}
+
+	/* Attach bpf program */
+	if (bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_DEVICE, 0)) {
+		printf("Failed to attach DEV_CGROUP program");
+		goto err;
+	}
+
+	if (bpf_prog_query(cgroup_fd, BPF_CGROUP_DEVICE, 0, NULL, NULL,
+			   &prog_cnt)) {
+		printf("Failed to query attached programs");
+		goto err;
+	}
+
+	/* All operations with /dev/zero and and /dev/urandom are allowed,
+	 * everything else is forbidden.
+	 */
+	assert(system("rm -f /tmp/test_dev_cgroup_null") == 0);
+	assert(system("mknod /tmp/test_dev_cgroup_null c 1 3"));
+	assert(system("rm -f /tmp/test_dev_cgroup_null") == 0);
+
+	/* /dev/zero is whitelisted */
+	assert(system("rm -f /tmp/test_dev_cgroup_zero") == 0);
+	assert(system("mknod /tmp/test_dev_cgroup_zero c 1 5") == 0);
+	assert(system("rm -f /tmp/test_dev_cgroup_zero") == 0);
+
+	assert(system("dd if=/dev/urandom of=/dev/zero count=64") == 0);
+
+	/* src is allowed, target is forbidden */
+	assert(system("dd if=/dev/urandom of=/dev/full count=64"));
+
+	/* src is forbidden, target is allowed */
+	assert(system("dd if=/dev/random of=/dev/zero count=64"));
+
+	error = 0;
+	printf("test_dev_cgroup:PASS\n");
+
+err:
+	cleanup_cgroup_environment();
+
+	return error;
+}
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 net-next 0/5] eBPF-based device cgroup controller
  2017-11-05 13:15   ` Roman Gushchin
                       ` (4 preceding siblings ...)
  2017-11-05 13:15     ` [PATCH v3 net-next 5/5] selftests/bpf: add a test for device cgroup controller Roman Gushchin
@ 2017-11-05 14:27     ` David Miller
  5 siblings, 0 replies; 16+ messages in thread
From: David Miller @ 2017-11-05 14:27 UTC (permalink / raw)
  To: guro; +Cc: netdev, tj, ast, daniel, linux-kernel, kernel-team

From: Roman Gushchin <guro@fb.com>
Date: Sun, 5 Nov 2017 08:15:29 -0500

> This patchset introduces an eBPF-based device controller for cgroup v2.
> 
> Patches (1) and (2) are a preparational work required to share some code
>   with the existing device controller implementation.
> Patch (3) is the main patch, which introduces a new bpf prog type
>   and all necessary infrastructure.
> Patch (4) moves cgroup_helpers.c/h to use them by patch (4).
> Patch (5) implements an example of eBPF program which controls access
>   to device files and corresponding userspace test.

Series applied, thank you.

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2017-11-05 14:27 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-02 17:15 [PATCH v3 net-next 0/5] eBPF-based device cgroup controller Roman Gushchin
2017-11-02 17:15 ` [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants Roman Gushchin
2017-11-02 17:54   ` Joe Perches
2017-11-02 20:06     ` Roman Gushchin
2017-11-02 17:15 ` [PATCH v3 net-next 2/5] device_cgroup: prepare code for bpf-based device controller Roman Gushchin
2017-11-02 17:15 ` [PATCH v3 net-next 3/5] bpf, cgroup: implement eBPF-based device controller for cgroup v2 Roman Gushchin
2017-11-02 17:15 ` [PATCH v3 net-next 4/5] bpf: move cgroup_helpers from samples/bpf/ to tools/testing/selftesting/bpf/ Roman Gushchin
2017-11-02 17:15 ` [PATCH v3 net-next 5/5] selftests/bpf: add a test for device cgroup controller Roman Gushchin
2017-11-04 13:40 ` [PATCH v3 net-next 0/5] eBPF-based " David Miller
2017-11-05 13:15   ` Roman Gushchin
2017-11-05 13:15     ` [PATCH v3 net-next 1/5] device_cgroup: add DEVCG_ prefix to ACC_* and DEV_* constants Roman Gushchin
2017-11-05 13:15     ` [PATCH v3 net-next 2/5] device_cgroup: prepare code for bpf-based device controller Roman Gushchin
2017-11-05 13:15     ` [PATCH v3 net-next 3/5] bpf, cgroup: implement eBPF-based device controller for cgroup v2 Roman Gushchin
2017-11-05 13:15     ` [PATCH v3 net-next 4/5] bpf: move cgroup_helpers from samples/bpf/ to tools/testing/selftesting/bpf/ Roman Gushchin
2017-11-05 13:15     ` [PATCH v3 net-next 5/5] selftests/bpf: add a test for device cgroup controller Roman Gushchin
2017-11-05 14:27     ` [PATCH v3 net-next 0/5] eBPF-based " David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).