Re: [PATCH v4 2/4] bpf: Add new bpf map type to store the pointer to struct perf_event

2015-07-29 Thread Alexei Starovoitov

On 7/29/15 4:30 PM, Daniel Borkmann wrote:

+if (map && map->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
+func_id != BPF_FUNC_perf_event_read)


this part belongs in patch 3, since patch 2 won't compile as-is.
Please keep bi-sectability intact.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


Re: [PATCH v4 2/4] bpf: Add new bpf map type to store the pointer to struct perf_event

2015-07-29 Thread Daniel Borkmann

On 07/28/2015 01:17 PM, Kaixu Xia wrote:

Introduce a new bpf map type 'BPF_MAP_TYPE_PERF_EVENT_ARRAY'.
This map only stores the pointer to struct perf_event. The
user space event FDs from perf_event_open() syscall are converted
to the pointer to struct perf_event and stored in map.

Signed-off-by: Kaixu Xia 
---
  include/linux/bpf.h|  1 +
  include/linux/perf_event.h |  2 ++
  include/uapi/linux/bpf.h   |  1 +
  kernel/bpf/arraymap.c  | 57 ++
  kernel/bpf/verifier.c  | 15 
  kernel/events/core.c   | 17 ++
  6 files changed, 93 insertions(+)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 610b730..3c9c0eb 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -10,6 +10,7 @@
  #include 
  #include 
  #include 
+#include 

  struct bpf_map;

diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 2027809..2ea4067 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -641,6 +641,7 @@ extern int perf_event_init_task(struct task_struct *child);
  extern void perf_event_exit_task(struct task_struct *child);
  extern void perf_event_free_task(struct task_struct *task);
  extern void perf_event_delayed_put(struct task_struct *task);
+extern struct perf_event *perf_event_get(unsigned int fd);
  extern void perf_event_print_debug(void);
  extern void perf_pmu_disable(struct pmu *pmu);
  extern void perf_pmu_enable(struct pmu *pmu);
@@ -979,6 +980,7 @@ static inline int perf_event_init_task(struct task_struct 
*child)   { return 0; }
  static inline void perf_event_exit_task(struct task_struct *child){ }
  static inline void perf_event_free_task(struct task_struct *task) { }
  static inline void perf_event_delayed_put(struct task_struct *task)   { }
+static struct perf_event *perf_event_get(unsigned int fd)  { 
return NULL; }
  static inline void perf_event_print_debug(void)   
{ }
  static inline int perf_event_task_disable(void)   
{ return -EINVAL; }
  static inline int perf_event_task_enable(void)
{ return -EINVAL; }
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 29ef6f9..69a1f6b 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -114,6 +114,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_HASH,
BPF_MAP_TYPE_ARRAY,
BPF_MAP_TYPE_PROG_ARRAY,
+   BPF_MAP_TYPE_PERF_EVENT_ARRAY,
  };

  enum bpf_prog_type {
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 4784cdc..a9e0235 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -297,3 +297,60 @@ static int __init register_prog_array_map(void)
return 0;
  }
  late_initcall(register_prog_array_map);
+
+static void *perf_event_fd_array_get_ptr(struct bpf_array *array, int fd)
+{
+   struct perf_event *event;
+
+   event = perf_event_get(fd);
+   if (IS_ERR(event))
+   return event;


So in case of !CONFIG_PERF_EVENTS, you define perf_event_get() to return NULL.

Seems like this will give a nice NULL ptr deref below. ;)


+   /*
+* prevent some crazy events so we can make our life easier
+*/
+   if (event->attr.type != PERF_TYPE_RAW &&
+   event->attr.type != PERF_TYPE_HARDWARE) {
+   perf_event_release_kernel(event);
+   return ERR_PTR(-EINVAL);
+   }
+   return event;
+}
+
+static void perf_event_fd_array_put_ptr(struct bpf_array *array, void *ptr)
+{
+   struct perf_event *event = (struct perf_event *)ptr;


( Same comment as in previous patch. )


+   perf_event_release_kernel(event);
+}
+
+static const struct fd_array_map_ops perf_event_fd_array_map_ops = {
+   .get_ptr= perf_event_fd_array_get_ptr,
+   .put_ptr= perf_event_fd_array_put_ptr,
+};

...

+late_initcall(register_perf_event_array_map);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 039d866..c70f7e7 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -924,6 +924,21 @@ static int check_call(struct verifier_env *env, int 
func_id)
 */
return -EINVAL;

+   if (map && map->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
+   func_id != BPF_FUNC_perf_event_read)
+   /* perf_event_array map type needs extra care:
+* only allow to pass it into bpf_perf_event_read() for now.
+* bpf_map_update/delete_elem() must only be done via syscall
+*/
+   return -EINVAL;
+
+   if (func_id == BPF_FUNC_perf_event_read &&
+   map->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
+   /* don't allow any other map type to be passed into
+* bpf_perf_event_read()
+*/
+   return -EINVAL;


Maybe a function as we do similar checks with BPF_MAP_TYPE_PROG_ARRAY?


 

Re: [PATCH v4 2/4] bpf: Add new bpf map type to store the pointer to struct perf_event

2015-07-29 Thread Daniel Borkmann

On 07/28/2015 01:17 PM, Kaixu Xia wrote:

Introduce a new bpf map type 'BPF_MAP_TYPE_PERF_EVENT_ARRAY'.
This map only stores the pointer to struct perf_event. The
user space event FDs from perf_event_open() syscall are converted
to the pointer to struct perf_event and stored in map.

Signed-off-by: Kaixu Xia xiaka...@huawei.com
---
  include/linux/bpf.h|  1 +
  include/linux/perf_event.h |  2 ++
  include/uapi/linux/bpf.h   |  1 +
  kernel/bpf/arraymap.c  | 57 ++
  kernel/bpf/verifier.c  | 15 
  kernel/events/core.c   | 17 ++
  6 files changed, 93 insertions(+)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 610b730..3c9c0eb 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -10,6 +10,7 @@
  #include uapi/linux/bpf.h
  #include linux/workqueue.h
  #include linux/file.h
+#include linux/perf_event.h

  struct bpf_map;

diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 2027809..2ea4067 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -641,6 +641,7 @@ extern int perf_event_init_task(struct task_struct *child);
  extern void perf_event_exit_task(struct task_struct *child);
  extern void perf_event_free_task(struct task_struct *task);
  extern void perf_event_delayed_put(struct task_struct *task);
+extern struct perf_event *perf_event_get(unsigned int fd);
  extern void perf_event_print_debug(void);
  extern void perf_pmu_disable(struct pmu *pmu);
  extern void perf_pmu_enable(struct pmu *pmu);
@@ -979,6 +980,7 @@ static inline int perf_event_init_task(struct task_struct 
*child)   { return 0; }
  static inline void perf_event_exit_task(struct task_struct *child){ }
  static inline void perf_event_free_task(struct task_struct *task) { }
  static inline void perf_event_delayed_put(struct task_struct *task)   { }
+static struct perf_event *perf_event_get(unsigned int fd)  { 
return NULL; }
  static inline void perf_event_print_debug(void)   
{ }
  static inline int perf_event_task_disable(void)   
{ return -EINVAL; }
  static inline int perf_event_task_enable(void)
{ return -EINVAL; }
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 29ef6f9..69a1f6b 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -114,6 +114,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_HASH,
BPF_MAP_TYPE_ARRAY,
BPF_MAP_TYPE_PROG_ARRAY,
+   BPF_MAP_TYPE_PERF_EVENT_ARRAY,
  };

  enum bpf_prog_type {
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 4784cdc..a9e0235 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -297,3 +297,60 @@ static int __init register_prog_array_map(void)
return 0;
  }
  late_initcall(register_prog_array_map);
+
+static void *perf_event_fd_array_get_ptr(struct bpf_array *array, int fd)
+{
+   struct perf_event *event;
+
+   event = perf_event_get(fd);
+   if (IS_ERR(event))
+   return event;


So in case of !CONFIG_PERF_EVENTS, you define perf_event_get() to return NULL.

Seems like this will give a nice NULL ptr deref below. ;)


+   /*
+* prevent some crazy events so we can make our life easier
+*/
+   if (event-attr.type != PERF_TYPE_RAW 
+   event-attr.type != PERF_TYPE_HARDWARE) {
+   perf_event_release_kernel(event);
+   return ERR_PTR(-EINVAL);
+   }
+   return event;
+}
+
+static void perf_event_fd_array_put_ptr(struct bpf_array *array, void *ptr)
+{
+   struct perf_event *event = (struct perf_event *)ptr;


( Same comment as in previous patch. )


+   perf_event_release_kernel(event);
+}
+
+static const struct fd_array_map_ops perf_event_fd_array_map_ops = {
+   .get_ptr= perf_event_fd_array_get_ptr,
+   .put_ptr= perf_event_fd_array_put_ptr,
+};

...

+late_initcall(register_perf_event_array_map);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 039d866..c70f7e7 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -924,6 +924,21 @@ static int check_call(struct verifier_env *env, int 
func_id)
 */
return -EINVAL;

+   if (map  map-map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY 
+   func_id != BPF_FUNC_perf_event_read)
+   /* perf_event_array map type needs extra care:
+* only allow to pass it into bpf_perf_event_read() for now.
+* bpf_map_update/delete_elem() must only be done via syscall
+*/
+   return -EINVAL;
+
+   if (func_id == BPF_FUNC_perf_event_read 
+   map-map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
+   /* don't allow any other map type to be passed into
+* bpf_perf_event_read()
+*/
+   return -EINVAL;


Maybe a 

Re: [PATCH v4 2/4] bpf: Add new bpf map type to store the pointer to struct perf_event

2015-07-29 Thread Alexei Starovoitov

On 7/29/15 4:30 PM, Daniel Borkmann wrote:

+if (map  map-map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY 
+func_id != BPF_FUNC_perf_event_read)


this part belongs in patch 3, since patch 2 won't compile as-is.
Please keep bi-sectability intact.

--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


[PATCH v4 2/4] bpf: Add new bpf map type to store the pointer to struct perf_event

2015-07-28 Thread Kaixu Xia
Introduce a new bpf map type 'BPF_MAP_TYPE_PERF_EVENT_ARRAY'.
This map only stores the pointer to struct perf_event. The
user space event FDs from perf_event_open() syscall are converted
to the pointer to struct perf_event and stored in map.

Signed-off-by: Kaixu Xia 
---
 include/linux/bpf.h|  1 +
 include/linux/perf_event.h |  2 ++
 include/uapi/linux/bpf.h   |  1 +
 kernel/bpf/arraymap.c  | 57 ++
 kernel/bpf/verifier.c  | 15 
 kernel/events/core.c   | 17 ++
 6 files changed, 93 insertions(+)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 610b730..3c9c0eb 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -10,6 +10,7 @@
 #include 
 #include 
 #include 
+#include 
 
 struct bpf_map;
 
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 2027809..2ea4067 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -641,6 +641,7 @@ extern int perf_event_init_task(struct task_struct *child);
 extern void perf_event_exit_task(struct task_struct *child);
 extern void perf_event_free_task(struct task_struct *task);
 extern void perf_event_delayed_put(struct task_struct *task);
+extern struct perf_event *perf_event_get(unsigned int fd);
 extern void perf_event_print_debug(void);
 extern void perf_pmu_disable(struct pmu *pmu);
 extern void perf_pmu_enable(struct pmu *pmu);
@@ -979,6 +980,7 @@ static inline int perf_event_init_task(struct task_struct 
*child)   { return 0; }
 static inline void perf_event_exit_task(struct task_struct *child) { }
 static inline void perf_event_free_task(struct task_struct *task)  { }
 static inline void perf_event_delayed_put(struct task_struct *task){ }
+static struct perf_event *perf_event_get(unsigned int fd)  { 
return NULL; }
 static inline void perf_event_print_debug(void)
{ }
 static inline int perf_event_task_disable(void)
{ return -EINVAL; }
 static inline int perf_event_task_enable(void) { 
return -EINVAL; }
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 29ef6f9..69a1f6b 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -114,6 +114,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_HASH,
BPF_MAP_TYPE_ARRAY,
BPF_MAP_TYPE_PROG_ARRAY,
+   BPF_MAP_TYPE_PERF_EVENT_ARRAY,
 };
 
 enum bpf_prog_type {
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 4784cdc..a9e0235 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -297,3 +297,60 @@ static int __init register_prog_array_map(void)
return 0;
 }
 late_initcall(register_prog_array_map);
+
+static void *perf_event_fd_array_get_ptr(struct bpf_array *array, int fd)
+{
+   struct perf_event *event;
+
+   event = perf_event_get(fd);
+   if (IS_ERR(event))
+   return event;
+
+   /*
+* prevent some crazy events so we can make our life easier
+*/
+   if (event->attr.type != PERF_TYPE_RAW &&
+   event->attr.type != PERF_TYPE_HARDWARE) {
+   perf_event_release_kernel(event);
+   return ERR_PTR(-EINVAL);
+   }
+   return event;
+}
+
+static void perf_event_fd_array_put_ptr(struct bpf_array *array, void *ptr)
+{
+   struct perf_event *event = (struct perf_event *)ptr;
+
+   perf_event_release_kernel(event);
+}
+
+static const struct fd_array_map_ops perf_event_fd_array_map_ops = {
+   .get_ptr= perf_event_fd_array_get_ptr,
+   .put_ptr= perf_event_fd_array_put_ptr,
+};
+
+static struct bpf_map *perf_event_array_map_alloc(union bpf_attr *attr)
+{
+   return fd_array_map_alloc(attr, _event_fd_array_map_ops);
+}
+
+static const struct bpf_map_ops perf_event_array_ops = {
+   .map_alloc = perf_event_array_map_alloc,
+   .map_free = fd_array_map_free,
+   .map_get_next_key = array_map_get_next_key,
+   .map_lookup_elem = fd_array_map_lookup_elem,
+   .map_update_elem = fd_array_map_update_elem,
+   .map_delete_elem = fd_array_map_delete_elem,
+};
+
+static struct bpf_map_type_list perf_event_array_type __read_mostly = {
+   .ops = _event_array_ops,
+   .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+};
+
+static int __init register_perf_event_array_map(void)
+{
+   bpf_register_map_type(_event_array_type);
+   return 0;
+}
+late_initcall(register_perf_event_array_map);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 039d866..c70f7e7 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -924,6 +924,21 @@ static int check_call(struct verifier_env *env, int 
func_id)
 */
return -EINVAL;
 
+   if (map && map->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
+   func_id != BPF_FUNC_perf_event_read)
+   /* perf_event_array map type needs extra care:
+   

[PATCH v4 2/4] bpf: Add new bpf map type to store the pointer to struct perf_event

2015-07-28 Thread Kaixu Xia
Introduce a new bpf map type 'BPF_MAP_TYPE_PERF_EVENT_ARRAY'.
This map only stores the pointer to struct perf_event. The
user space event FDs from perf_event_open() syscall are converted
to the pointer to struct perf_event and stored in map.

Signed-off-by: Kaixu Xia xiaka...@huawei.com
---
 include/linux/bpf.h|  1 +
 include/linux/perf_event.h |  2 ++
 include/uapi/linux/bpf.h   |  1 +
 kernel/bpf/arraymap.c  | 57 ++
 kernel/bpf/verifier.c  | 15 
 kernel/events/core.c   | 17 ++
 6 files changed, 93 insertions(+)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 610b730..3c9c0eb 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -10,6 +10,7 @@
 #include uapi/linux/bpf.h
 #include linux/workqueue.h
 #include linux/file.h
+#include linux/perf_event.h
 
 struct bpf_map;
 
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 2027809..2ea4067 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -641,6 +641,7 @@ extern int perf_event_init_task(struct task_struct *child);
 extern void perf_event_exit_task(struct task_struct *child);
 extern void perf_event_free_task(struct task_struct *task);
 extern void perf_event_delayed_put(struct task_struct *task);
+extern struct perf_event *perf_event_get(unsigned int fd);
 extern void perf_event_print_debug(void);
 extern void perf_pmu_disable(struct pmu *pmu);
 extern void perf_pmu_enable(struct pmu *pmu);
@@ -979,6 +980,7 @@ static inline int perf_event_init_task(struct task_struct 
*child)   { return 0; }
 static inline void perf_event_exit_task(struct task_struct *child) { }
 static inline void perf_event_free_task(struct task_struct *task)  { }
 static inline void perf_event_delayed_put(struct task_struct *task){ }
+static struct perf_event *perf_event_get(unsigned int fd)  { 
return NULL; }
 static inline void perf_event_print_debug(void)
{ }
 static inline int perf_event_task_disable(void)
{ return -EINVAL; }
 static inline int perf_event_task_enable(void) { 
return -EINVAL; }
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 29ef6f9..69a1f6b 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -114,6 +114,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_HASH,
BPF_MAP_TYPE_ARRAY,
BPF_MAP_TYPE_PROG_ARRAY,
+   BPF_MAP_TYPE_PERF_EVENT_ARRAY,
 };
 
 enum bpf_prog_type {
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 4784cdc..a9e0235 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -297,3 +297,60 @@ static int __init register_prog_array_map(void)
return 0;
 }
 late_initcall(register_prog_array_map);
+
+static void *perf_event_fd_array_get_ptr(struct bpf_array *array, int fd)
+{
+   struct perf_event *event;
+
+   event = perf_event_get(fd);
+   if (IS_ERR(event))
+   return event;
+
+   /*
+* prevent some crazy events so we can make our life easier
+*/
+   if (event-attr.type != PERF_TYPE_RAW 
+   event-attr.type != PERF_TYPE_HARDWARE) {
+   perf_event_release_kernel(event);
+   return ERR_PTR(-EINVAL);
+   }
+   return event;
+}
+
+static void perf_event_fd_array_put_ptr(struct bpf_array *array, void *ptr)
+{
+   struct perf_event *event = (struct perf_event *)ptr;
+
+   perf_event_release_kernel(event);
+}
+
+static const struct fd_array_map_ops perf_event_fd_array_map_ops = {
+   .get_ptr= perf_event_fd_array_get_ptr,
+   .put_ptr= perf_event_fd_array_put_ptr,
+};
+
+static struct bpf_map *perf_event_array_map_alloc(union bpf_attr *attr)
+{
+   return fd_array_map_alloc(attr, perf_event_fd_array_map_ops);
+}
+
+static const struct bpf_map_ops perf_event_array_ops = {
+   .map_alloc = perf_event_array_map_alloc,
+   .map_free = fd_array_map_free,
+   .map_get_next_key = array_map_get_next_key,
+   .map_lookup_elem = fd_array_map_lookup_elem,
+   .map_update_elem = fd_array_map_update_elem,
+   .map_delete_elem = fd_array_map_delete_elem,
+};
+
+static struct bpf_map_type_list perf_event_array_type __read_mostly = {
+   .ops = perf_event_array_ops,
+   .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+};
+
+static int __init register_perf_event_array_map(void)
+{
+   bpf_register_map_type(perf_event_array_type);
+   return 0;
+}
+late_initcall(register_perf_event_array_map);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 039d866..c70f7e7 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -924,6 +924,21 @@ static int check_call(struct verifier_env *env, int 
func_id)
 */
return -EINVAL;
 
+   if (map  map-map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY 
+   func_id !=