[PATCH bpf-next v5 3/9] libbpf: Add support for task local storage

2020-11-05 Thread KP Singh
From: KP Singh 

Updates the bpf_probe_map_type API to also support
BPF_MAP_TYPE_TASK_STORAGE similar to other local storage maps.

Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 tools/lib/bpf/libbpf_probes.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index 5482a9b7ae2d..ecaae2927ab8 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -230,6 +230,7 @@ bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 
ifindex)
break;
case BPF_MAP_TYPE_SK_STORAGE:
case BPF_MAP_TYPE_INODE_STORAGE:
+   case BPF_MAP_TYPE_TASK_STORAGE:
btf_key_type_id = 1;
btf_value_type_id = 3;
value_size = 8;
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v5 1/9] bpf: Allow LSM programs to use bpf spin locks

2020-11-05 Thread KP Singh
From: KP Singh 

Usage of spin locks was not allowed for tracing programs due to
insufficient preemption checks. The verifier does not currently prevent
LSM programs from using spin locks, but the helpers are not exposed
via bpf_lsm_func_proto.

Based on the discussion in [1], non-sleepable LSM programs should be
able to use bpf_spin_{lock, unlock}.

Sleepable LSM programs can be preempted which means that allowng spin
locks will need more work (disabling preemption and the verifier
ensuring that no sleepable helpers are called when a spin lock is held).

[1]: 
https://lore.kernel.org/bpf/20201103153132.2717326-1-kpsi...@chromium.org/T/#md601a053229287659071600d3483523f752cd2fb

Acked-by: Song Liu 
Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 kernel/bpf/bpf_lsm.c  |  4 
 kernel/bpf/verifier.c | 20 +++-
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 78ea8a7bd27f..cd8a617f2109 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -59,6 +59,10 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct 
bpf_prog *prog)
return _sk_storage_get_proto;
case BPF_FUNC_sk_storage_delete:
return _sk_storage_delete_proto;
+   case BPF_FUNC_spin_lock:
+   return _spin_lock_proto;
+   case BPF_FUNC_spin_unlock:
+   return _spin_unlock_proto;
default:
return tracing_prog_func_proto(func_id, prog);
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6200519582a6..f863aa84d0a2 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9719,11 +9719,21 @@ static int check_map_prog_compatibility(struct 
bpf_verifier_env *env,
verbose(env, "trace type programs with run-time allocated hash 
maps are unsafe. Switch to preallocated hash maps.\n");
}
 
-   if ((is_tracing_prog_type(prog_type) ||
-prog_type == BPF_PROG_TYPE_SOCKET_FILTER) &&
-   map_value_has_spin_lock(map)) {
-   verbose(env, "tracing progs cannot use bpf_spin_lock yet\n");
-   return -EINVAL;
+   if (map_value_has_spin_lock(map)) {
+   if (prog_type == BPF_PROG_TYPE_SOCKET_FILTER) {
+   verbose(env, "socket filter progs cannot use 
bpf_spin_lock yet\n");
+   return -EINVAL;
+   }
+
+   if (is_tracing_prog_type(prog_type)) {
+   verbose(env, "tracing progs cannot use bpf_spin_lock 
yet\n");
+   return -EINVAL;
+   }
+
+   if (prog->aux->sleepable) {
+   verbose(env, "sleepable progs cannot use bpf_spin_lock 
yet\n");
+   return -EINVAL;
+   }
}
 
if ((bpf_prog_is_dev_bound(prog->aux) || bpf_map_is_dev_bound(map)) &&
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v5 9/9] bpf: Exercise syscall operations for inode and sk storage

2020-11-05 Thread KP Singh
From: KP Singh 

Use the check_syscall_operations added for task_local_storage to
exercise syscall operations for other local storage maps:

* Check the absence of an element for the given fd.
* Create a new element, retrieve and compare its value.
* Delete the element and check again for absence.

Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c | 17 +++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
index 5b4788b97d96..2c1b4e9c9a76 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -152,7 +152,7 @@ static bool check_syscall_operations(int map_fd, int obj_fd)
 void test_test_local_storage(void)
 {
char tmp_exec_path[PATH_MAX] = "/tmp/copy_of_rmXX";
-   int err, serv_sk = -1, task_fd = -1;
+   int err, serv_sk = -1, task_fd = -1, rm_fd = -1;
struct local_storage *skel = NULL;
 
skel = local_storage__open_and_load();
@@ -176,6 +176,15 @@ void test_test_local_storage(void)
if (CHECK(err < 0, "copy_rm", "err %d errno %d\n", err, errno))
goto close_prog;
 
+   rm_fd = open(tmp_exec_path, O_RDONLY);
+   if (CHECK(rm_fd < 0, "open", "failed to open %s err:%d, errno:%d",
+ tmp_exec_path, rm_fd, errno))
+   goto close_prog;
+
+   if (!check_syscall_operations(bpf_map__fd(skel->maps.inode_storage_map),
+ rm_fd))
+   goto close_prog;
+
/* Sets skel->bss->monitored_pid to the pid of the forked child
 * forks a child process that executes tmp_exec_path and tries to
 * unlink its executable. This operation should be denied by the loaded
@@ -204,11 +213,15 @@ void test_test_local_storage(void)
CHECK(skel->data->sk_storage_result != 0, "sk_storage_result",
  "sk_local_storage not set\n");
 
-   close(serv_sk);
+   if (!check_syscall_operations(bpf_map__fd(skel->maps.sk_storage_map),
+ serv_sk))
+   goto close_prog;
 
 close_prog_unlink:
unlink(tmp_exec_path);
 close_prog:
+   close(serv_sk);
+   close(rm_fd);
close(task_fd);
local_storage__destroy(skel);
 }
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v5 7/9] bpf: Update selftests for local_storage to use vmlinux.h

2020-11-05 Thread KP Singh
From: KP Singh 

With the fixing of BTF pruning of embedded types being fixed, the test
can be simplified to use vmlinux.h

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 .../selftests/bpf/progs/local_storage.c   | 20 +--
 1 file changed, 1 insertion(+), 19 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
index 09529e33be98..ef3822bc7542 100644
--- a/tools/testing/selftests/bpf/progs/local_storage.c
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -4,9 +4,8 @@
  * Copyright 2020 Google LLC.
  */
 
+#include "vmlinux.h"
 #include 
-#include 
-#include 
 #include 
 #include 
 
@@ -36,23 +35,6 @@ struct {
__type(value, struct dummy_storage);
 } sk_storage_map SEC(".maps");
 
-/* TODO Use vmlinux.h once BTF pruning for embedded types is fixed.
- */
-struct sock {} __attribute__((preserve_access_index));
-struct sockaddr {} __attribute__((preserve_access_index));
-struct socket {
-   struct sock *sk;
-} __attribute__((preserve_access_index));
-
-struct inode {} __attribute__((preserve_access_index));
-struct dentry {
-   struct inode *d_inode;
-} __attribute__((preserve_access_index));
-struct file {
-   struct inode *f_inode;
-} __attribute__((preserve_access_index));
-
-
 SEC("lsm/inode_unlink")
 int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
 {
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v5 8/9] bpf: Add tests for task_local_storage

2020-11-05 Thread KP Singh
From: KP Singh 

The test exercises the syscall based map operations by creating a pidfd
for the current process.

For verifying kernel / LSM functionality, the test implements a simple
MAC policy which denies an executable from unlinking itself. The LSM
program bprm_committed_creds sets a task_local_storage with a pointer to
the inode. This is then used to detect if the task is trying to unlink
itself in the inode_unlink LSM hook.

The test copies /bin/rm to /tmp and executes it in a child thread with
the intention of deleting itself. A successful test should prevent the
the running executable from deleting itself.

The bpf programs are also updated to call bpf_spin_{lock, unlock} to
trigger the verfier checks for spin locks.

The temporary file is cleaned up later in the test.

Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c   | 180 --
 .../selftests/bpf/progs/local_storage.c   |  61 +-
 2 files changed, 221 insertions(+), 20 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
index 91cd6f357246..5b4788b97d96 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -4,30 +4,156 @@
  * Copyright (C) 2020 Google LLC.
  */
 
+#define _GNU_SOURCE
+
+#include 
+#include 
+#include 
 #include 
 #include 
 
 #include "local_storage.skel.h"
 #include "network_helpers.h"
 
-int create_and_unlink_file(void)
+static inline int sys_pidfd_open(pid_t pid, unsigned int flags)
+{
+   return syscall(__NR_pidfd_open, pid, flags);
+}
+
+static unsigned int duration;
+
+#define TEST_STORAGE_VALUE 0xbeefdead
+
+struct storage {
+   void *inode;
+   unsigned int value;
+   /* Lock ensures that spin locked versions of local stoage operations
+* also work, most operations in this tests are still single threaded
+*/
+   struct bpf_spin_lock lock;
+};
+
+/* Copies an rm binary to a temp file. dest is a mkstemp template */
+static int copy_rm(char *dest)
 {
-   char fname[PATH_MAX] = "/tmp/fileXX";
-   int fd;
+   int fd_in, fd_out = -1, ret = 0;
+   struct stat stat;
+
+   fd_in = open("/bin/rm", O_RDONLY);
+   if (fd_in < 0)
+   return -errno;
+
+   fd_out = mkstemp(dest);
+   if (fd_out < 0) {
+   ret = -errno;
+   goto out;
+   }
+
+   ret = fstat(fd_in, );
+   if (ret == -1) {
+   ret = -errno;
+   goto out;
+   }
+
+   ret = copy_file_range(fd_in, NULL, fd_out, NULL, stat.st_size, 0);
+   if (ret == -1) {
+   ret = -errno;
+   goto out;
+   }
+
+   /* Set executable permission on the copied file */
+   ret = chmod(dest, 0100);
+   if (ret == -1)
+   ret = -errno;
+
+out:
+   close(fd_in);
+   close(fd_out);
+   return ret;
+}
 
-   fd = mkstemp(fname);
-   if (fd < 0)
-   return fd;
+/* Fork and exec the provided rm binary and return the exit code of the
+ * forked process and its pid.
+ */
+static int run_self_unlink(int *monitored_pid, const char *rm_path)
+{
+   int child_pid, child_status, ret;
+   int null_fd;
+
+   child_pid = fork();
+   if (child_pid == 0) {
+   null_fd = open("/dev/null", O_WRONLY);
+   dup2(null_fd, STDOUT_FILENO);
+   dup2(null_fd, STDERR_FILENO);
+   close(null_fd);
+
+   *monitored_pid = getpid();
+   /* Use the copied /usr/bin/rm to delete itself
+* /tmp/copy_of_rm /tmp/copy_of_rm.
+*/
+   ret = execlp(rm_path, rm_path, rm_path, NULL);
+   if (ret)
+   exit(errno);
+   } else if (child_pid > 0) {
+   waitpid(child_pid, _status, 0);
+   return WEXITSTATUS(child_status);
+   }
+
+   return -EINVAL;
+}
 
-   close(fd);
-   unlink(fname);
-   return 0;
+static bool check_syscall_operations(int map_fd, int obj_fd)
+{
+   struct storage val = { .value = TEST_STORAGE_VALUE, .lock = { 0 } },
+  lookup_val = { .value = 0, .lock = { 0 } };
+   int err;
+
+   /* Looking up an existing element should fail initially */
+   err = bpf_map_lookup_elem_flags(map_fd, _fd, _val,
+   BPF_F_LOCK);
+   if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem",
+ "err:%d errno:%d\n", err, errno))
+   return false;
+
+   /* Create a new element */
+   err = bpf_map_update_elem(map_fd, _fd, ,
+ BPF_NOEXIST | BPF_F_LOCK);
+   if (CHECK(err < 0, "bpf_map_update_elem", "err:%d errno:%d\n", e

[PATCH bpf-next v5 2/9] bpf: Implement task local storage

2020-11-05 Thread KP Singh
From: KP Singh 

Similar to bpf_local_storage for sockets and inodes add local storage
for task_struct.

The life-cycle of storage is managed with the life-cycle of the
task_struct.  i.e. the storage is destroyed along with the owning task
with a callback to the bpf_task_storage_free from the task_free LSM
hook.

The BPF LSM allocates an __rcu pointer to the bpf_local_storage in
the security blob which are now stackable and can co-exist with other
LSMs.

The userspace map operations can be done by using a pid fd as a key
passed to the lookup, update and delete operations.

Acked-by: Song Liu 
Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 include/linux/bpf_lsm.h|  23 +++
 include/linux/bpf_types.h  |   1 +
 include/uapi/linux/bpf.h   |  39 
 kernel/bpf/Makefile|   1 +
 kernel/bpf/bpf_lsm.c   |   4 +
 kernel/bpf/bpf_task_storage.c  | 315 +
 kernel/bpf/syscall.c   |   3 +-
 kernel/bpf/verifier.c  |  10 ++
 security/bpf/hooks.c   |   2 +
 tools/include/uapi/linux/bpf.h |  39 
 10 files changed, 436 insertions(+), 1 deletion(-)
 create mode 100644 kernel/bpf/bpf_task_storage.c

diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index aaacb6aafc87..73226181b744 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -7,6 +7,7 @@
 #ifndef _LINUX_BPF_LSM_H
 #define _LINUX_BPF_LSM_H
 
+#include 
 #include 
 #include 
 
@@ -35,9 +36,21 @@ static inline struct bpf_storage_blob *bpf_inode(
return inode->i_security + bpf_lsm_blob_sizes.lbs_inode;
 }
 
+static inline struct bpf_storage_blob *bpf_task(
+   const struct task_struct *task)
+{
+   if (unlikely(!task->security))
+   return NULL;
+
+   return task->security + bpf_lsm_blob_sizes.lbs_task;
+}
+
 extern const struct bpf_func_proto bpf_inode_storage_get_proto;
 extern const struct bpf_func_proto bpf_inode_storage_delete_proto;
+extern const struct bpf_func_proto bpf_task_storage_get_proto;
+extern const struct bpf_func_proto bpf_task_storage_delete_proto;
 void bpf_inode_storage_free(struct inode *inode);
+void bpf_task_storage_free(struct task_struct *task);
 
 #else /* !CONFIG_BPF_LSM */
 
@@ -53,10 +66,20 @@ static inline struct bpf_storage_blob *bpf_inode(
return NULL;
 }
 
+static inline struct bpf_storage_blob *bpf_task(
+   const struct task_struct *task)
+{
+   return NULL;
+}
+
 static inline void bpf_inode_storage_free(struct inode *inode)
 {
 }
 
+static inline void bpf_task_storage_free(struct task_struct *task)
+{
+}
+
 #endif /* CONFIG_BPF_LSM */
 
 #endif /* _LINUX_BPF_LSM_H */
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 2e6f568377f1..99f7fd657d87 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -109,6 +109,7 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKHASH, sock_hash_ops)
 #endif
 #ifdef CONFIG_BPF_LSM
 BPF_MAP_TYPE(BPF_MAP_TYPE_INODE_STORAGE, inode_storage_map_ops)
+BPF_MAP_TYPE(BPF_MAP_TYPE_TASK_STORAGE, task_storage_map_ops)
 #endif
 BPF_MAP_TYPE(BPF_MAP_TYPE_CPUMAP, cpu_map_ops)
 #if defined(CONFIG_XDP_SOCKETS)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index e6ceac3f7d62..f4037b2161a6 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -157,6 +157,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_STRUCT_OPS,
BPF_MAP_TYPE_RINGBUF,
BPF_MAP_TYPE_INODE_STORAGE,
+   BPF_MAP_TYPE_TASK_STORAGE,
 };
 
 /* Note that tracing related programs such as
@@ -3742,6 +3743,42 @@ union bpf_attr {
  * Return
  * The helper returns **TC_ACT_REDIRECT** on success or
  * **TC_ACT_SHOT** on error.
+ *
+ * void *bpf_task_storage_get(struct bpf_map *map, struct task_struct *task, 
void *value, u64 flags)
+ * Description
+ * Get a bpf_local_storage from the *task*.
+ *
+ * Logically, it could be thought of as getting the value from
+ * a *map* with *task* as the **key**.  From this
+ * perspective,  the usage is not much different from
+ * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this
+ * helper enforces the key must be an task_struct and the map must 
also
+ * be a **BPF_MAP_TYPE_TASK_STORAGE**.
+ *
+ * Underneath, the value is stored locally at *task* instead of
+ * the *map*.  The *map* is used as the bpf-local-storage
+ * "type". The bpf-local-storage "type" (i.e. the *map*) is
+ * searched against all bpf_local_storage residing at *task*.
+ *
+ * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be
+ * used such that a new bpf_local_storage will be
+ * created if one does not exist.  *value* can be used
+ * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify
+ * the initial value o

[PATCH bpf-next v5 4/9] bpftool: Add support for task local storage

2020-11-05 Thread KP Singh
From: KP Singh 

Updates the binary to handle the BPF_MAP_TYPE_TASK_STORAGE as
"task_storage" for printing and parsing. Also updates the documentation
and bash completion

Acked-by: Song Liu 
Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 tools/bpf/bpftool/Documentation/bpftool-map.rst | 3 ++-
 tools/bpf/bpftool/bash-completion/bpftool   | 2 +-
 tools/bpf/bpftool/map.c | 4 +++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst 
b/tools/bpf/bpftool/Documentation/bpftool-map.rst
index dade10cdf295..3d52256ba75f 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-map.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst
@@ -50,7 +50,8 @@ MAP COMMANDS
 |  | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | 
**hash_of_maps**
 |  | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | 
**xskmap** | **sockhash**
 |  | **cgroup_storage** | **reuseport_sockarray** | 
**percpu_cgroup_storage**
-|  | **queue** | **stack** | **sk_storage** | **struct_ops** | 
**ringbuf** | **inode_storage** }
+|  | **queue** | **stack** | **sk_storage** | **struct_ops** | 
**ringbuf** | **inode_storage**
+   | **task_storage** }
 
 DESCRIPTION
 ===
diff --git a/tools/bpf/bpftool/bash-completion/bpftool 
b/tools/bpf/bpftool/bash-completion/bpftool
index 3f1da30c4da6..fdffbc64c65c 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -705,7 +705,7 @@ _bpftool()
 hash_of_maps devmap devmap_hash sockmap cpumap 
\
 xskmap sockhash cgroup_storage 
reuseport_sockarray \
 percpu_cgroup_storage queue stack sk_storage \
-struct_ops inode_storage' -- \
+struct_ops inode_storage task_storage' -- \
"$cur" ) )
 return 0
 ;;
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index a7efbd84fbcc..b400364ee054 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -51,6 +51,7 @@ const char * const map_type_name[] = {
[BPF_MAP_TYPE_STRUCT_OPS]   = "struct_ops",
[BPF_MAP_TYPE_RINGBUF]  = "ringbuf",
[BPF_MAP_TYPE_INODE_STORAGE]= "inode_storage",
+   [BPF_MAP_TYPE_TASK_STORAGE] = "task_storage",
 };
 
 const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
@@ -1464,7 +1465,8 @@ static int do_help(int argc, char **argv)
" lru_percpu_hash | lpm_trie | array_of_maps | 
hash_of_maps |\n"
" devmap | devmap_hash | sockmap | cpumap | 
xskmap | sockhash |\n"
" cgroup_storage | reuseport_sockarray | 
percpu_cgroup_storage |\n"
-   " queue | stack | sk_storage | struct_ops | 
ringbuf | inode_storage }\n"
+   " queue | stack | sk_storage | struct_ops | 
ringbuf | inode_storage |\n"
+   " task_storage }\n"
"   " HELP_SPEC_OPTIONS "\n"
"",
bin_name, argv[-2]);
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v5 6/9] bpf: Fix tests for local_storage

2020-11-05 Thread KP Singh
From: KP Singh 

The {inode,sk}_storage_result checking if the correct value was retrieved
was being clobbered unconditionally by the return value of the
bpf_{inode,sk}_storage_delete call.

Also, consistently use the newly added BPF_LOCAL_STORAGE_GET_F_CREATE
flag.

Acked-by: Song Liu 
Fixes: cd324d7abb3d ("bpf: Add selftests for local_storage")
Signed-off-by: KP Singh 
---
 .../selftests/bpf/progs/local_storage.c   | 24 ---
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
index 0758ba229ae0..09529e33be98 100644
--- a/tools/testing/selftests/bpf/progs/local_storage.c
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -58,20 +58,22 @@ int BPF_PROG(unlink_hook, struct inode *dir, struct dentry 
*victim)
 {
__u32 pid = bpf_get_current_pid_tgid() >> 32;
struct dummy_storage *storage;
+   int err;
 
if (pid != monitored_pid)
return 0;
 
storage = bpf_inode_storage_get(_storage_map, victim->d_inode, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+   BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-   if (storage->value == DUMMY_STORAGE_VALUE)
+   if (storage->value != DUMMY_STORAGE_VALUE)
inode_storage_result = -1;
 
-   inode_storage_result =
-   bpf_inode_storage_delete(_storage_map, victim->d_inode);
+   err = bpf_inode_storage_delete(_storage_map, victim->d_inode);
+   if (!err)
+   inode_storage_result = err;
 
return 0;
 }
@@ -82,19 +84,23 @@ int BPF_PROG(socket_bind, struct socket *sock, struct 
sockaddr *address,
 {
__u32 pid = bpf_get_current_pid_tgid() >> 32;
struct dummy_storage *storage;
+   int err;
 
if (pid != monitored_pid)
return 0;
 
storage = bpf_sk_storage_get(_storage_map, sock->sk, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-   if (storage->value == DUMMY_STORAGE_VALUE)
+   if (storage->value != DUMMY_STORAGE_VALUE)
sk_storage_result = -1;
 
-   sk_storage_result = bpf_sk_storage_delete(_storage_map, sock->sk);
+   err = bpf_sk_storage_delete(_storage_map, sock->sk);
+   if (!err)
+   sk_storage_result = err;
+
return 0;
 }
 
@@ -109,7 +115,7 @@ int BPF_PROG(socket_post_create, struct socket *sock, int 
family, int type,
return 0;
 
storage = bpf_sk_storage_get(_storage_map, sock->sk, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
@@ -131,7 +137,7 @@ int BPF_PROG(file_open, struct file *file)
return 0;
 
storage = bpf_inode_storage_get(_storage_map, file->f_inode, 0,
-BPF_LOCAL_STORAGE_GET_F_CREATE);
+   BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v5 5/9] bpf: Implement get_current_task_btf and RET_PTR_TO_BTF_ID

2020-11-05 Thread KP Singh
From: KP Singh 

The currently available bpf_get_current_task returns an unsigned integer
which can be used along with BPF_CORE_READ to read data from
the task_struct but still cannot be used as an input argument to a
helper that accepts an ARG_PTR_TO_BTF_ID of type task_struct.

In order to implement this helper a new return type, RET_PTR_TO_BTF_ID,
is added. This is similar to RET_PTR_TO_BTF_ID_OR_NULL but does not
require checking the nullness of returned pointer.

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 include/linux/bpf.h|  1 +
 include/uapi/linux/bpf.h   |  9 +
 kernel/bpf/verifier.c  |  7 +--
 kernel/trace/bpf_trace.c   | 16 
 tools/include/uapi/linux/bpf.h |  9 +
 5 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2fffd30e13ac..73d5381a5d5c 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -310,6 +310,7 @@ enum bpf_return_type {
RET_PTR_TO_BTF_ID_OR_NULL,  /* returns a pointer to a btf_id or 
NULL */
RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL, /* returns a pointer to a valid 
memory or a btf_id or NULL */
RET_PTR_TO_MEM_OR_BTF_ID,   /* returns a pointer to a valid memory 
or a btf_id */
+   RET_PTR_TO_BTF_ID,  /* returns a pointer to a btf_id */
 };
 
 /* eBPF function prototype used by verifier to allow BPF_CALLs from eBPF 
programs
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index f4037b2161a6..9879d6793e90 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -3779,6 +3779,14 @@ union bpf_attr {
  * 0 on success.
  *
  * **-ENOENT** if the bpf_local_storage cannot be found.
+ *
+ * struct task_struct *bpf_get_current_task_btf(void)
+ * Description
+ * Return a BTF pointer to the "current" task.
+ * This pointer can also be used in helpers that accept an
+ * *ARG_PTR_TO_BTF_ID* of type *task_struct*.
+ * Return
+ * Pointer to the current task.
  */
 #define __BPF_FUNC_MAPPER(FN)  \
FN(unspec), \
@@ -3939,6 +3947,7 @@ union bpf_attr {
FN(redirect_peer),  \
FN(task_storage_get),   \
FN(task_storage_delete),\
+   FN(get_current_task_btf),   \
/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 00960f6a83ec..10da26e55130 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5186,11 +5186,14 @@ static int check_helper_call(struct bpf_verifier_env 
*env, int func_id, int insn
PTR_TO_BTF_ID : PTR_TO_BTF_ID_OR_NULL;
regs[BPF_REG_0].btf_id = meta.ret_btf_id;
}
-   } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL) {
+   } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL ||
+  fn->ret_type == RET_PTR_TO_BTF_ID) {
int ret_btf_id;
 
mark_reg_known_zero(env, regs, BPF_REG_0);
-   regs[BPF_REG_0].type = PTR_TO_BTF_ID_OR_NULL;
+   regs[BPF_REG_0].type = fn->ret_type == RET_PTR_TO_BTF_ID ?
+PTR_TO_BTF_ID :
+PTR_TO_BTF_ID_OR_NULL;
ret_btf_id = *fn->ret_btf_id;
if (ret_btf_id == 0) {
verbose(env, "invalid return type %d of func %s#%d\n",
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 4517c8b66518..e4515b0f62a8 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1022,6 +1022,20 @@ const struct bpf_func_proto bpf_get_current_task_proto = 
{
.ret_type   = RET_INTEGER,
 };
 
+BPF_CALL_0(bpf_get_current_task_btf)
+{
+   return (unsigned long) current;
+}
+
+BTF_ID_LIST_SINGLE(bpf_get_current_btf_ids, struct, task_struct)
+
+static const struct bpf_func_proto bpf_get_current_task_btf_proto = {
+   .func   = bpf_get_current_task_btf,
+   .gpl_only   = true,
+   .ret_type   = RET_PTR_TO_BTF_ID,
+   .ret_btf_id = _get_current_btf_ids[0],
+};
+
 BPF_CALL_2(bpf_current_task_under_cgroup, struct bpf_map *, map, u32, idx)
 {
struct bpf_array *array = container_of(map, struct bpf_array, map);
@@ -1265,6 +1279,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const 
struct bpf_prog *prog)
return _get_current_pid_tgid_proto;
case BPF_FUNC_get_current_task:
return _get_current_task_proto;
+   case BPF_FUNC_get_current_task_btf:
+   return _get_current_task_btf_proto;
case BPF_FUNC_get_current_uid_gid:
return _get_current_uid_gid_proto;
case BPF_FUNC_get_current_comm:
diff --git

[PATCH bpf-next v5 0/9] Implement task_local_storage

2020-11-05 Thread KP Singh
From: KP Singh 

# v4 -> v5

- Fixes to selftests as suggested by Martin.
- Added Martin's acks.

# v3 -> v4

- Move the patch that exposes spin lock helpers to LSM programs as the
  first patch as some of the changes in the implementation are actually
  for spin locks.
- Clarify the comment in the bpf_task_storage_{get, delete} helper as
  discussed with Martin.
- Added Martin's ack and rebased.

# v2 -> v3

- Added bpf_spin_locks to the selftests for local storage, found that
  these are not available for LSM programs.
- Made spin lock helpers available for LSM programs (except sleepable
  programs which need more work).
- Minor fixes for includes and added short commit messages for patches
  that were split up for libbpf and bpftool.
- Added Song's acks.

# v1 -> v2

- Updated the refcounting for task_struct and simplified conversion
  of fd -> struct pid.
- Some fixes suggested by Martin and Andrii, notably:
   * long return type for the bpf_task_storage_delete helper (update
 for bpf_inode_storage_delete will be sent separately).
   * Remove extra nullness check to task_storage_ptr in map syscall
 ops.
   * Changed the argument signature of the BPF helpers to use
 task_struct pointer in uapi headers.
   * Remove unnecessary verifier logic for the bpf_get_current_task_btf
 helper.
   * Split the changes for bpftool and libbpf.
- Exercised syscall operations for local storage (kept a simpler verison
  in test_local_storage.c, the eventual goal will be to update
  sk_storage_map.c for all local storage types).
- Formatting fixes + Rebase.

We already have socket and inode local storage since [1]

This patch series:

* Implements bpf_local_storage for task_struct.
* Implements the bpf_get_current_task_btf helper which returns a BTF
  pointer to the current task. Not only is this generally cleaner
  (reading from the task_struct currently requires BPF_CORE_READ), it
  also allows the BTF pointer to be used in task_local_storage helpers.
* In order to implement this helper, a RET_PTR_TO_BTF_ID is introduced
  which works similar to RET_PTR_TO_BTF_ID_OR_NULL but does not require
  a nullness check.
* Implements a detection in selftests which uses the
  task local storage to deny a running executable from unlinking itself.

[1]: 
https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=f836a56e84ffc9f1a1cd73f77e10404ca46a4616


KP Singh (9):
  bpf: Allow LSM programs to use bpf spin locks
  bpf: Implement task local storage
  libbpf: Add support for task local storage
  bpftool: Add support for task local storage
  bpf: Implement get_current_task_btf and RET_PTR_TO_BTF_ID
  bpf: Fix tests for local_storage
  bpf: Update selftests for local_storage to use vmlinux.h
  bpf: Add tests for task_local_storage
  bpf: Exercise syscall operations for inode and sk storage

 include/linux/bpf.h   |   1 +
 include/linux/bpf_lsm.h   |  23 ++
 include/linux/bpf_types.h |   1 +
 include/uapi/linux/bpf.h  |  48 +++
 kernel/bpf/Makefile   |   1 +
 kernel/bpf/bpf_lsm.c  |   8 +
 kernel/bpf/bpf_task_storage.c | 315 ++
 kernel/bpf/syscall.c  |   3 +-
 kernel/bpf/verifier.c |  37 +-
 kernel/trace/bpf_trace.c  |  16 +
 security/bpf/hooks.c  |   2 +
 .../bpf/bpftool/Documentation/bpftool-map.rst |   3 +-
 tools/bpf/bpftool/bash-completion/bpftool |   2 +-
 tools/bpf/bpftool/map.c   |   4 +-
 tools/include/uapi/linux/bpf.h|  48 +++
 tools/lib/bpf/libbpf_probes.c |   1 +
 .../bpf/prog_tests/test_local_storage.c   | 195 ++-
 .../selftests/bpf/progs/local_storage.c   | 103 --
 18 files changed, 752 insertions(+), 59 deletions(-)
 create mode 100644 kernel/bpf/bpf_task_storage.c

-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next] bpf: Update verification logic for LSM programs

2020-11-05 Thread KP Singh
From: KP Singh 

The current logic checks if the name of the BTF type passed in
attach_btf_id starts with "bpf_lsm_", this is not sufficient as it also
allows attachment to non-LSM hooks like the very function that performs
this check, i.e. bpf_lsm_verify_prog.

In order to ensure that this verification logic allows attachment to
only LSM hooks, the LSM_HOOK definitions in lsm_hook_defs.h are used to
generate a BTD id set. The attach_btf_id of the program being attached
is then checked for presence in this set.

Signed-off-by: KP Singh 
---
 kernel/bpf/bpf_lsm.c | 10 +++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 78ea8a7bd27f..56cc5a915f67 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -13,6 +13,7 @@
 #include 
 #include 
 #include 
+#include 
 
 /* For every LSM hook that allows attachment of BPF programs, declare a nop
  * function where a BPF program can be attached.
@@ -26,7 +27,11 @@ noinline RET bpf_lsm_##NAME(__VA_ARGS__) \
 #include 
 #undef LSM_HOOK
 
-#define BPF_LSM_SYM_PREFX  "bpf_lsm_"
+#define LSM_HOOK(RET, DEFAULT, NAME, ...) BTF_ID(func, bpf_lsm_##NAME)
+BTF_SET_START(bpf_lsm_hooks)
+#include 
+#undef LSM_HOOK
+BTF_SET_END(bpf_lsm_hooks)
 
 int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
const struct bpf_prog *prog)
@@ -37,8 +42,7 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
return -EINVAL;
}
 
-   if (strncmp(BPF_LSM_SYM_PREFX, prog->aux->attach_func_name,
-   sizeof(BPF_LSM_SYM_PREFX) - 1)) {
+   if (!btf_id_set_contains(_lsm_hooks, prog->aux->attach_btf_id)) {
bpf_log(vlog, "attach_btf_id %u points to wrong type name %s\n",
prog->aux->attach_btf_id, prog->aux->attach_func_name);
return -EINVAL;
-- 
2.29.1.341.ge80a0c044ae-goog



Re: [PATCH bpf-next] bpf: Update verification logic for LSM programs

2020-11-05 Thread KP Singh
On Fri, Nov 6, 2020 at 12:02 AM KP Singh  wrote:
>
> From: KP Singh 
>
> The current logic checks if the name of the BTF type passed in
> attach_btf_id starts with "bpf_lsm_", this is not sufficient as it also
> allows attachment to non-LSM hooks like the very function that performs
> this check, i.e. bpf_lsm_verify_prog.
>
> In order to ensure that this verification logic allows attachment to
> only LSM hooks, the LSM_HOOK definitions in lsm_hook_defs.h are used to
> generate a BTD id set. The attach_btf_id of the program being attached

Fixing typo (BTD -> BTF) and resending.


[PATCH bpf-next v2] bpf: Update verification logic for LSM programs

2020-11-05 Thread KP Singh
From: KP Singh 

The current logic checks if the name of the BTF type passed in
attach_btf_id starts with "bpf_lsm_", this is not sufficient as it also
allows attachment to non-LSM hooks like the very function that performs
this check, i.e. bpf_lsm_verify_prog.

In order to ensure that this verification logic allows attachment to
only LSM hooks, the LSM_HOOK definitions in lsm_hook_defs.h are used to
generate a BTF_ID set. Upon verification, the attach_btf_id of the
program being attached is checked for presence in this set.

Signed-off-by: KP Singh 
---
 kernel/bpf/bpf_lsm.c | 10 +++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 78ea8a7bd27f..56cc5a915f67 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -13,6 +13,7 @@
 #include 
 #include 
 #include 
+#include 
 
 /* For every LSM hook that allows attachment of BPF programs, declare a nop
  * function where a BPF program can be attached.
@@ -26,7 +27,11 @@ noinline RET bpf_lsm_##NAME(__VA_ARGS__) \
 #include 
 #undef LSM_HOOK
 
-#define BPF_LSM_SYM_PREFX  "bpf_lsm_"
+#define LSM_HOOK(RET, DEFAULT, NAME, ...) BTF_ID(func, bpf_lsm_##NAME)
+BTF_SET_START(bpf_lsm_hooks)
+#include 
+#undef LSM_HOOK
+BTF_SET_END(bpf_lsm_hooks)
 
 int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
const struct bpf_prog *prog)
@@ -37,8 +42,7 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
return -EINVAL;
}
 
-   if (strncmp(BPF_LSM_SYM_PREFX, prog->aux->attach_func_name,
-   sizeof(BPF_LSM_SYM_PREFX) - 1)) {
+   if (!btf_id_set_contains(_lsm_hooks, prog->aux->attach_btf_id)) {
bpf_log(vlog, "attach_btf_id %u points to wrong type name %s\n",
prog->aux->attach_btf_id, prog->aux->attach_func_name);
return -EINVAL;
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v6 9/9] bpf: Exercise syscall operations for inode and sk storage

2020-11-06 Thread KP Singh
From: KP Singh 

Use the check_syscall_operations added for task_local_storage to
exercise syscall operations for other local storage maps:

* Check the absence of an element for the given fd.
* Create a new element, retrieve and compare its value.
* Delete the element and check again for absence.

Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c | 17 +++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
index 4e7f6a4965f2..5fda45982be0 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -157,7 +157,7 @@ static bool check_syscall_operations(int map_fd, int obj_fd)
 void test_test_local_storage(void)
 {
char tmp_exec_path[PATH_MAX] = "/tmp/copy_of_rmXX";
-   int err, serv_sk = -1, task_fd = -1;
+   int err, serv_sk = -1, task_fd = -1, rm_fd = -1;
struct local_storage *skel = NULL;
 
skel = local_storage__open_and_load();
@@ -181,6 +181,15 @@ void test_test_local_storage(void)
if (CHECK(err < 0, "copy_rm", "err %d errno %d\n", err, errno))
goto close_prog;
 
+   rm_fd = open(tmp_exec_path, O_RDONLY);
+   if (CHECK(rm_fd < 0, "open", "failed to open %s err:%d, errno:%d",
+ tmp_exec_path, rm_fd, errno))
+   goto close_prog;
+
+   if (!check_syscall_operations(bpf_map__fd(skel->maps.inode_storage_map),
+ rm_fd))
+   goto close_prog;
+
/* Sets skel->bss->monitored_pid to the pid of the forked child
 * forks a child process that executes tmp_exec_path and tries to
 * unlink its executable. This operation should be denied by the loaded
@@ -209,11 +218,15 @@ void test_test_local_storage(void)
CHECK(skel->data->sk_storage_result != 0, "sk_storage_result",
  "sk_local_storage not set\n");
 
-   close(serv_sk);
+   if (!check_syscall_operations(bpf_map__fd(skel->maps.sk_storage_map),
+ serv_sk))
+   goto close_prog;
 
 close_prog_unlink:
unlink(tmp_exec_path);
 close_prog:
+   close(serv_sk);
+   close(rm_fd);
close(task_fd);
local_storage__destroy(skel);
 }
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v6 1/9] bpf: Allow LSM programs to use bpf spin locks

2020-11-06 Thread KP Singh
From: KP Singh 

Usage of spin locks was not allowed for tracing programs due to
insufficient preemption checks. The verifier does not currently prevent
LSM programs from using spin locks, but the helpers are not exposed
via bpf_lsm_func_proto.

Based on the discussion in [1], non-sleepable LSM programs should be
able to use bpf_spin_{lock, unlock}.

Sleepable LSM programs can be preempted which means that allowng spin
locks will need more work (disabling preemption and the verifier
ensuring that no sleepable helpers are called when a spin lock is held).

[1]: 
https://lore.kernel.org/bpf/20201103153132.2717326-1-kpsi...@chromium.org/T/#md601a053229287659071600d3483523f752cd2fb

Acked-by: Song Liu 
Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 kernel/bpf/bpf_lsm.c  |  4 
 kernel/bpf/verifier.c | 20 +++-
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 78ea8a7bd27f..cd8a617f2109 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -59,6 +59,10 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct 
bpf_prog *prog)
return _sk_storage_get_proto;
case BPF_FUNC_sk_storage_delete:
return _sk_storage_delete_proto;
+   case BPF_FUNC_spin_lock:
+   return _spin_lock_proto;
+   case BPF_FUNC_spin_unlock:
+   return _spin_unlock_proto;
default:
return tracing_prog_func_proto(func_id, prog);
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6200519582a6..f863aa84d0a2 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9719,11 +9719,21 @@ static int check_map_prog_compatibility(struct 
bpf_verifier_env *env,
verbose(env, "trace type programs with run-time allocated hash 
maps are unsafe. Switch to preallocated hash maps.\n");
}
 
-   if ((is_tracing_prog_type(prog_type) ||
-prog_type == BPF_PROG_TYPE_SOCKET_FILTER) &&
-   map_value_has_spin_lock(map)) {
-   verbose(env, "tracing progs cannot use bpf_spin_lock yet\n");
-   return -EINVAL;
+   if (map_value_has_spin_lock(map)) {
+   if (prog_type == BPF_PROG_TYPE_SOCKET_FILTER) {
+   verbose(env, "socket filter progs cannot use 
bpf_spin_lock yet\n");
+   return -EINVAL;
+   }
+
+   if (is_tracing_prog_type(prog_type)) {
+   verbose(env, "tracing progs cannot use bpf_spin_lock 
yet\n");
+   return -EINVAL;
+   }
+
+   if (prog->aux->sleepable) {
+   verbose(env, "sleepable progs cannot use bpf_spin_lock 
yet\n");
+   return -EINVAL;
+   }
}
 
if ((bpf_prog_is_dev_bound(prog->aux) || bpf_map_is_dev_bound(map)) &&
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v6 5/9] bpf: Implement get_current_task_btf and RET_PTR_TO_BTF_ID

2020-11-06 Thread KP Singh
From: KP Singh 

The currently available bpf_get_current_task returns an unsigned integer
which can be used along with BPF_CORE_READ to read data from
the task_struct but still cannot be used as an input argument to a
helper that accepts an ARG_PTR_TO_BTF_ID of type task_struct.

In order to implement this helper a new return type, RET_PTR_TO_BTF_ID,
is added. This is similar to RET_PTR_TO_BTF_ID_OR_NULL but does not
require checking the nullness of returned pointer.

Acked-by: Song Liu 
Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 include/linux/bpf.h|  1 +
 include/uapi/linux/bpf.h   |  9 +
 kernel/bpf/verifier.c  |  7 +--
 kernel/trace/bpf_trace.c   | 16 
 tools/include/uapi/linux/bpf.h |  9 +
 5 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2fffd30e13ac..73d5381a5d5c 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -310,6 +310,7 @@ enum bpf_return_type {
RET_PTR_TO_BTF_ID_OR_NULL,  /* returns a pointer to a btf_id or 
NULL */
RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL, /* returns a pointer to a valid 
memory or a btf_id or NULL */
RET_PTR_TO_MEM_OR_BTF_ID,   /* returns a pointer to a valid memory 
or a btf_id */
+   RET_PTR_TO_BTF_ID,  /* returns a pointer to a btf_id */
 };
 
 /* eBPF function prototype used by verifier to allow BPF_CALLs from eBPF 
programs
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index f4037b2161a6..9879d6793e90 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -3779,6 +3779,14 @@ union bpf_attr {
  * 0 on success.
  *
  * **-ENOENT** if the bpf_local_storage cannot be found.
+ *
+ * struct task_struct *bpf_get_current_task_btf(void)
+ * Description
+ * Return a BTF pointer to the "current" task.
+ * This pointer can also be used in helpers that accept an
+ * *ARG_PTR_TO_BTF_ID* of type *task_struct*.
+ * Return
+ * Pointer to the current task.
  */
 #define __BPF_FUNC_MAPPER(FN)  \
FN(unspec), \
@@ -3939,6 +3947,7 @@ union bpf_attr {
FN(redirect_peer),  \
FN(task_storage_get),   \
FN(task_storage_delete),\
+   FN(get_current_task_btf),   \
/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 00960f6a83ec..10da26e55130 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5186,11 +5186,14 @@ static int check_helper_call(struct bpf_verifier_env 
*env, int func_id, int insn
PTR_TO_BTF_ID : PTR_TO_BTF_ID_OR_NULL;
regs[BPF_REG_0].btf_id = meta.ret_btf_id;
}
-   } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL) {
+   } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL ||
+  fn->ret_type == RET_PTR_TO_BTF_ID) {
int ret_btf_id;
 
mark_reg_known_zero(env, regs, BPF_REG_0);
-   regs[BPF_REG_0].type = PTR_TO_BTF_ID_OR_NULL;
+   regs[BPF_REG_0].type = fn->ret_type == RET_PTR_TO_BTF_ID ?
+PTR_TO_BTF_ID :
+PTR_TO_BTF_ID_OR_NULL;
ret_btf_id = *fn->ret_btf_id;
if (ret_btf_id == 0) {
verbose(env, "invalid return type %d of func %s#%d\n",
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 4517c8b66518..e4515b0f62a8 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1022,6 +1022,20 @@ const struct bpf_func_proto bpf_get_current_task_proto = 
{
.ret_type   = RET_INTEGER,
 };
 
+BPF_CALL_0(bpf_get_current_task_btf)
+{
+   return (unsigned long) current;
+}
+
+BTF_ID_LIST_SINGLE(bpf_get_current_btf_ids, struct, task_struct)
+
+static const struct bpf_func_proto bpf_get_current_task_btf_proto = {
+   .func   = bpf_get_current_task_btf,
+   .gpl_only   = true,
+   .ret_type   = RET_PTR_TO_BTF_ID,
+   .ret_btf_id = _get_current_btf_ids[0],
+};
+
 BPF_CALL_2(bpf_current_task_under_cgroup, struct bpf_map *, map, u32, idx)
 {
struct bpf_array *array = container_of(map, struct bpf_array, map);
@@ -1265,6 +1279,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const 
struct bpf_prog *prog)
return _get_current_pid_tgid_proto;
case BPF_FUNC_get_current_task:
return _get_current_task_proto;
+   case BPF_FUNC_get_current_task_btf:
+   return _get_current_task_btf_proto;
case BPF_FUNC_get_current_uid_gid:
return _get_current_uid_gid_proto;
case BPF_FUNC_g

[PATCH bpf-next v6 0/9] Implement task_local_storage

2020-11-06 Thread KP Singh
From: KP Singh 

# v5 -> v6

- Using a wrapper for copy_file_range in selftests since it's missing
  in older libcs.
- Added Martin's acks.

# v4 -> v5

- Fixes to selftests as suggested by Martin.
- Added Martin's acks.

# v3 -> v4

- Move the patch that exposes spin lock helpers to LSM programs as the
  first patch as some of the changes in the implementation are actually
  for spin locks.
- Clarify the comment in the bpf_task_storage_{get, delete} helper as
  discussed with Martin.
- Added Martin's ack and rebased.

# v2 -> v3

- Added bpf_spin_locks to the selftests for local storage, found that
  these are not available for LSM programs.
- Made spin lock helpers available for LSM programs (except sleepable
  programs which need more work).
- Minor fixes for includes and added short commit messages for patches
  that were split up for libbpf and bpftool.
- Added Song's acks.

# v1 -> v2

- Updated the refcounting for task_struct and simplified conversion
  of fd -> struct pid.
- Some fixes suggested by Martin and Andrii, notably:
   * long return type for the bpf_task_storage_delete helper (update
 for bpf_inode_storage_delete will be sent separately).
   * Remove extra nullness check to task_storage_ptr in map syscall
 ops.
   * Changed the argument signature of the BPF helpers to use
 task_struct pointer in uapi headers.
   * Remove unnecessary verifier logic for the bpf_get_current_task_btf
 helper.
   * Split the changes for bpftool and libbpf.
- Exercised syscall operations for local storage (kept a simpler verison
  in test_local_storage.c, the eventual goal will be to update
  sk_storage_map.c for all local storage types).
- Formatting fixes + Rebase.

We already have socket and inode local storage since [1]

This patch series:

* Implements bpf_local_storage for task_struct.
* Implements the bpf_get_current_task_btf helper which returns a BTF
  pointer to the current task. Not only is this generally cleaner
  (reading from the task_struct currently requires BPF_CORE_READ), it
  also allows the BTF pointer to be used in task_local_storage helpers.
* In order to implement this helper, a RET_PTR_TO_BTF_ID is introduced
  which works similar to RET_PTR_TO_BTF_ID_OR_NULL but does not require
  a nullness check.
* Implements a detection in selftests which uses the
  task local storage to deny a running executable from unlinking itself.

[1]: 
https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=f836a56e84ffc9f1a1cd73f77e10404ca46a4616

KP Singh (9):
  bpf: Allow LSM programs to use bpf spin locks
  bpf: Implement task local storage
  libbpf: Add support for task local storage
  bpftool: Add support for task local storage
  bpf: Implement get_current_task_btf and RET_PTR_TO_BTF_ID
  bpf: Fix tests for local_storage
  bpf: Update selftests for local_storage to use vmlinux.h
  bpf: Add tests for task_local_storage
  bpf: Exercise syscall operations for inode and sk storage

 include/linux/bpf.h   |   1 +
 include/linux/bpf_lsm.h   |  23 ++
 include/linux/bpf_types.h |   1 +
 include/uapi/linux/bpf.h  |  48 +++
 kernel/bpf/Makefile   |   1 +
 kernel/bpf/bpf_lsm.c  |   8 +
 kernel/bpf/bpf_task_storage.c | 315 ++
 kernel/bpf/syscall.c  |   3 +-
 kernel/bpf/verifier.c |  37 +-
 kernel/trace/bpf_trace.c  |  16 +
 security/bpf/hooks.c  |   2 +
 .../bpf/bpftool/Documentation/bpftool-map.rst |   3 +-
 tools/bpf/bpftool/bash-completion/bpftool |   2 +-
 tools/bpf/bpftool/map.c   |   4 +-
 tools/include/uapi/linux/bpf.h|  48 +++
 tools/lib/bpf/libbpf_probes.c |   1 +
 .../bpf/prog_tests/test_local_storage.c   | 200 ++-
 .../selftests/bpf/progs/local_storage.c   | 103 --
 18 files changed, 757 insertions(+), 59 deletions(-)
 create mode 100644 kernel/bpf/bpf_task_storage.c

-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v6 6/9] bpf: Fix tests for local_storage

2020-11-06 Thread KP Singh
From: KP Singh 

The {inode,sk}_storage_result checking if the correct value was retrieved
was being clobbered unconditionally by the return value of the
bpf_{inode,sk}_storage_delete call.

Also, consistently use the newly added BPF_LOCAL_STORAGE_GET_F_CREATE
flag.

Acked-by: Song Liu 
Fixes: cd324d7abb3d ("bpf: Add selftests for local_storage")
Signed-off-by: KP Singh 
---
 .../selftests/bpf/progs/local_storage.c   | 24 ---
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
index 0758ba229ae0..09529e33be98 100644
--- a/tools/testing/selftests/bpf/progs/local_storage.c
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -58,20 +58,22 @@ int BPF_PROG(unlink_hook, struct inode *dir, struct dentry 
*victim)
 {
__u32 pid = bpf_get_current_pid_tgid() >> 32;
struct dummy_storage *storage;
+   int err;
 
if (pid != monitored_pid)
return 0;
 
storage = bpf_inode_storage_get(_storage_map, victim->d_inode, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+   BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-   if (storage->value == DUMMY_STORAGE_VALUE)
+   if (storage->value != DUMMY_STORAGE_VALUE)
inode_storage_result = -1;
 
-   inode_storage_result =
-   bpf_inode_storage_delete(_storage_map, victim->d_inode);
+   err = bpf_inode_storage_delete(_storage_map, victim->d_inode);
+   if (!err)
+   inode_storage_result = err;
 
return 0;
 }
@@ -82,19 +84,23 @@ int BPF_PROG(socket_bind, struct socket *sock, struct 
sockaddr *address,
 {
__u32 pid = bpf_get_current_pid_tgid() >> 32;
struct dummy_storage *storage;
+   int err;
 
if (pid != monitored_pid)
return 0;
 
storage = bpf_sk_storage_get(_storage_map, sock->sk, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-   if (storage->value == DUMMY_STORAGE_VALUE)
+   if (storage->value != DUMMY_STORAGE_VALUE)
sk_storage_result = -1;
 
-   sk_storage_result = bpf_sk_storage_delete(_storage_map, sock->sk);
+   err = bpf_sk_storage_delete(_storage_map, sock->sk);
+   if (!err)
+   sk_storage_result = err;
+
return 0;
 }
 
@@ -109,7 +115,7 @@ int BPF_PROG(socket_post_create, struct socket *sock, int 
family, int type,
return 0;
 
storage = bpf_sk_storage_get(_storage_map, sock->sk, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
@@ -131,7 +137,7 @@ int BPF_PROG(file_open, struct file *file)
return 0;
 
storage = bpf_inode_storage_get(_storage_map, file->f_inode, 0,
-BPF_LOCAL_STORAGE_GET_F_CREATE);
+   BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v6 8/9] bpf: Add tests for task_local_storage

2020-11-06 Thread KP Singh
From: KP Singh 

The test exercises the syscall based map operations by creating a pidfd
for the current process.

For verifying kernel / LSM functionality, the test implements a simple
MAC policy which denies an executable from unlinking itself. The LSM
program bprm_committed_creds sets a task_local_storage with a pointer to
the inode. This is then used to detect if the task is trying to unlink
itself in the inode_unlink LSM hook.

The test copies /bin/rm to /tmp and executes it in a child thread with
the intention of deleting itself. A successful test should prevent the
the running executable from deleting itself.

The bpf programs are also updated to call bpf_spin_{lock, unlock} to
trigger the verfier checks for spin locks.

The temporary file is cleaned up later in the test.

Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c   | 185 --
 .../selftests/bpf/progs/local_storage.c   |  61 +-
 2 files changed, 226 insertions(+), 20 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
index 91cd6f357246..4e7f6a4965f2 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -4,30 +4,161 @@
  * Copyright (C) 2020 Google LLC.
  */
 
+#include 
+#include 
 #include 
 #include 
 
 #include "local_storage.skel.h"
 #include "network_helpers.h"
 
-int create_and_unlink_file(void)
+static inline int sys_pidfd_open(pid_t pid, unsigned int flags)
 {
-   char fname[PATH_MAX] = "/tmp/fileXX";
-   int fd;
+   return syscall(__NR_pidfd_open, pid, flags);
+}
+
+static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, int fd_out,
+ loff_t *off_out, size_t len,
+ unsigned int flags)
+{
+   return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out,
+  len, flags);
+}
+
+static unsigned int duration;
+
+#define TEST_STORAGE_VALUE 0xbeefdead
 
-   fd = mkstemp(fname);
-   if (fd < 0)
-   return fd;
+struct storage {
+   void *inode;
+   unsigned int value;
+   /* Lock ensures that spin locked versions of local stoage operations
+* also work, most operations in this tests are still single threaded
+*/
+   struct bpf_spin_lock lock;
+};
+
+/* Copies an rm binary to a temp file. dest is a mkstemp template */
+static int copy_rm(char *dest)
+{
+   int fd_in, fd_out = -1, ret = 0;
+   struct stat stat;
+
+   fd_in = open("/bin/rm", O_RDONLY);
+   if (fd_in < 0)
+   return -errno;
+
+   fd_out = mkstemp(dest);
+   if (fd_out < 0) {
+   ret = -errno;
+   goto out;
+   }
+
+   ret = fstat(fd_in, );
+   if (ret == -1) {
+   ret = -errno;
+   goto out;
+   }
+
+   ret = copy_file_range(fd_in, NULL, fd_out, NULL, stat.st_size, 0);
+   if (ret == -1) {
+   ret = -errno;
+   goto out;
+   }
+
+   /* Set executable permission on the copied file */
+   ret = chmod(dest, 0100);
+   if (ret == -1)
+   ret = -errno;
+
+out:
+   close(fd_in);
+   close(fd_out);
+   return ret;
+}
+
+/* Fork and exec the provided rm binary and return the exit code of the
+ * forked process and its pid.
+ */
+static int run_self_unlink(int *monitored_pid, const char *rm_path)
+{
+   int child_pid, child_status, ret;
+   int null_fd;
+
+   child_pid = fork();
+   if (child_pid == 0) {
+   null_fd = open("/dev/null", O_WRONLY);
+   dup2(null_fd, STDOUT_FILENO);
+   dup2(null_fd, STDERR_FILENO);
+   close(null_fd);
+
+   *monitored_pid = getpid();
+   /* Use the copied /usr/bin/rm to delete itself
+* /tmp/copy_of_rm /tmp/copy_of_rm.
+*/
+   ret = execlp(rm_path, rm_path, rm_path, NULL);
+   if (ret)
+   exit(errno);
+   } else if (child_pid > 0) {
+   waitpid(child_pid, _status, 0);
+   return WEXITSTATUS(child_status);
+   }
+
+   return -EINVAL;
+}
 
-   close(fd);
-   unlink(fname);
-   return 0;
+static bool check_syscall_operations(int map_fd, int obj_fd)
+{
+   struct storage val = { .value = TEST_STORAGE_VALUE, .lock = { 0 } },
+  lookup_val = { .value = 0, .lock = { 0 } };
+   int err;
+
+   /* Looking up an existing element should fail initially */
+   err = bpf_map_lookup_elem_flags(map_fd, _fd, _val,
+   BPF_F_LOCK);
+   if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem",
+   

[PATCH bpf-next v6 7/9] bpf: Update selftests for local_storage to use vmlinux.h

2020-11-06 Thread KP Singh
From: KP Singh 

With the fixing of BTF pruning of embedded types being fixed, the test
can be simplified to use vmlinux.h

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 .../selftests/bpf/progs/local_storage.c   | 20 +--
 1 file changed, 1 insertion(+), 19 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
index 09529e33be98..ef3822bc7542 100644
--- a/tools/testing/selftests/bpf/progs/local_storage.c
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -4,9 +4,8 @@
  * Copyright 2020 Google LLC.
  */
 
+#include "vmlinux.h"
 #include 
-#include 
-#include 
 #include 
 #include 
 
@@ -36,23 +35,6 @@ struct {
__type(value, struct dummy_storage);
 } sk_storage_map SEC(".maps");
 
-/* TODO Use vmlinux.h once BTF pruning for embedded types is fixed.
- */
-struct sock {} __attribute__((preserve_access_index));
-struct sockaddr {} __attribute__((preserve_access_index));
-struct socket {
-   struct sock *sk;
-} __attribute__((preserve_access_index));
-
-struct inode {} __attribute__((preserve_access_index));
-struct dentry {
-   struct inode *d_inode;
-} __attribute__((preserve_access_index));
-struct file {
-   struct inode *f_inode;
-} __attribute__((preserve_access_index));
-
-
 SEC("lsm/inode_unlink")
 int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
 {
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v6 3/9] libbpf: Add support for task local storage

2020-11-06 Thread KP Singh
From: KP Singh 

Updates the bpf_probe_map_type API to also support
BPF_MAP_TYPE_TASK_STORAGE similar to other local storage maps.

Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 tools/lib/bpf/libbpf_probes.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index 5482a9b7ae2d..ecaae2927ab8 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -230,6 +230,7 @@ bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 
ifindex)
break;
case BPF_MAP_TYPE_SK_STORAGE:
case BPF_MAP_TYPE_INODE_STORAGE:
+   case BPF_MAP_TYPE_TASK_STORAGE:
btf_key_type_id = 1;
btf_value_type_id = 3;
value_size = 8;
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v6 2/9] bpf: Implement task local storage

2020-11-06 Thread KP Singh
From: KP Singh 

Similar to bpf_local_storage for sockets and inodes add local storage
for task_struct.

The life-cycle of storage is managed with the life-cycle of the
task_struct.  i.e. the storage is destroyed along with the owning task
with a callback to the bpf_task_storage_free from the task_free LSM
hook.

The BPF LSM allocates an __rcu pointer to the bpf_local_storage in
the security blob which are now stackable and can co-exist with other
LSMs.

The userspace map operations can be done by using a pid fd as a key
passed to the lookup, update and delete operations.

Acked-by: Song Liu 
Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 include/linux/bpf_lsm.h|  23 +++
 include/linux/bpf_types.h  |   1 +
 include/uapi/linux/bpf.h   |  39 
 kernel/bpf/Makefile|   1 +
 kernel/bpf/bpf_lsm.c   |   4 +
 kernel/bpf/bpf_task_storage.c  | 315 +
 kernel/bpf/syscall.c   |   3 +-
 kernel/bpf/verifier.c  |  10 ++
 security/bpf/hooks.c   |   2 +
 tools/include/uapi/linux/bpf.h |  39 
 10 files changed, 436 insertions(+), 1 deletion(-)
 create mode 100644 kernel/bpf/bpf_task_storage.c

diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index aaacb6aafc87..73226181b744 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -7,6 +7,7 @@
 #ifndef _LINUX_BPF_LSM_H
 #define _LINUX_BPF_LSM_H
 
+#include 
 #include 
 #include 
 
@@ -35,9 +36,21 @@ static inline struct bpf_storage_blob *bpf_inode(
return inode->i_security + bpf_lsm_blob_sizes.lbs_inode;
 }
 
+static inline struct bpf_storage_blob *bpf_task(
+   const struct task_struct *task)
+{
+   if (unlikely(!task->security))
+   return NULL;
+
+   return task->security + bpf_lsm_blob_sizes.lbs_task;
+}
+
 extern const struct bpf_func_proto bpf_inode_storage_get_proto;
 extern const struct bpf_func_proto bpf_inode_storage_delete_proto;
+extern const struct bpf_func_proto bpf_task_storage_get_proto;
+extern const struct bpf_func_proto bpf_task_storage_delete_proto;
 void bpf_inode_storage_free(struct inode *inode);
+void bpf_task_storage_free(struct task_struct *task);
 
 #else /* !CONFIG_BPF_LSM */
 
@@ -53,10 +66,20 @@ static inline struct bpf_storage_blob *bpf_inode(
return NULL;
 }
 
+static inline struct bpf_storage_blob *bpf_task(
+   const struct task_struct *task)
+{
+   return NULL;
+}
+
 static inline void bpf_inode_storage_free(struct inode *inode)
 {
 }
 
+static inline void bpf_task_storage_free(struct task_struct *task)
+{
+}
+
 #endif /* CONFIG_BPF_LSM */
 
 #endif /* _LINUX_BPF_LSM_H */
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 2e6f568377f1..99f7fd657d87 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -109,6 +109,7 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKHASH, sock_hash_ops)
 #endif
 #ifdef CONFIG_BPF_LSM
 BPF_MAP_TYPE(BPF_MAP_TYPE_INODE_STORAGE, inode_storage_map_ops)
+BPF_MAP_TYPE(BPF_MAP_TYPE_TASK_STORAGE, task_storage_map_ops)
 #endif
 BPF_MAP_TYPE(BPF_MAP_TYPE_CPUMAP, cpu_map_ops)
 #if defined(CONFIG_XDP_SOCKETS)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index e6ceac3f7d62..f4037b2161a6 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -157,6 +157,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_STRUCT_OPS,
BPF_MAP_TYPE_RINGBUF,
BPF_MAP_TYPE_INODE_STORAGE,
+   BPF_MAP_TYPE_TASK_STORAGE,
 };
 
 /* Note that tracing related programs such as
@@ -3742,6 +3743,42 @@ union bpf_attr {
  * Return
  * The helper returns **TC_ACT_REDIRECT** on success or
  * **TC_ACT_SHOT** on error.
+ *
+ * void *bpf_task_storage_get(struct bpf_map *map, struct task_struct *task, 
void *value, u64 flags)
+ * Description
+ * Get a bpf_local_storage from the *task*.
+ *
+ * Logically, it could be thought of as getting the value from
+ * a *map* with *task* as the **key**.  From this
+ * perspective,  the usage is not much different from
+ * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this
+ * helper enforces the key must be an task_struct and the map must 
also
+ * be a **BPF_MAP_TYPE_TASK_STORAGE**.
+ *
+ * Underneath, the value is stored locally at *task* instead of
+ * the *map*.  The *map* is used as the bpf-local-storage
+ * "type". The bpf-local-storage "type" (i.e. the *map*) is
+ * searched against all bpf_local_storage residing at *task*.
+ *
+ * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be
+ * used such that a new bpf_local_storage will be
+ * created if one does not exist.  *value* can be used
+ * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify
+ * the initial value o

[PATCH bpf-next v6 4/9] bpftool: Add support for task local storage

2020-11-06 Thread KP Singh
From: KP Singh 

Updates the binary to handle the BPF_MAP_TYPE_TASK_STORAGE as
"task_storage" for printing and parsing. Also updates the documentation
and bash completion

Acked-by: Song Liu 
Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 tools/bpf/bpftool/Documentation/bpftool-map.rst | 3 ++-
 tools/bpf/bpftool/bash-completion/bpftool   | 2 +-
 tools/bpf/bpftool/map.c | 4 +++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst 
b/tools/bpf/bpftool/Documentation/bpftool-map.rst
index dade10cdf295..3d52256ba75f 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-map.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst
@@ -50,7 +50,8 @@ MAP COMMANDS
 |  | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | 
**hash_of_maps**
 |  | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | 
**xskmap** | **sockhash**
 |  | **cgroup_storage** | **reuseport_sockarray** | 
**percpu_cgroup_storage**
-|  | **queue** | **stack** | **sk_storage** | **struct_ops** | 
**ringbuf** | **inode_storage** }
+|  | **queue** | **stack** | **sk_storage** | **struct_ops** | 
**ringbuf** | **inode_storage**
+   | **task_storage** }
 
 DESCRIPTION
 ===
diff --git a/tools/bpf/bpftool/bash-completion/bpftool 
b/tools/bpf/bpftool/bash-completion/bpftool
index 3f1da30c4da6..fdffbc64c65c 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -705,7 +705,7 @@ _bpftool()
 hash_of_maps devmap devmap_hash sockmap cpumap 
\
 xskmap sockhash cgroup_storage 
reuseport_sockarray \
 percpu_cgroup_storage queue stack sk_storage \
-struct_ops inode_storage' -- \
+struct_ops inode_storage task_storage' -- \
"$cur" ) )
 return 0
 ;;
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index a7efbd84fbcc..b400364ee054 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -51,6 +51,7 @@ const char * const map_type_name[] = {
[BPF_MAP_TYPE_STRUCT_OPS]   = "struct_ops",
[BPF_MAP_TYPE_RINGBUF]  = "ringbuf",
[BPF_MAP_TYPE_INODE_STORAGE]= "inode_storage",
+   [BPF_MAP_TYPE_TASK_STORAGE] = "task_storage",
 };
 
 const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
@@ -1464,7 +1465,8 @@ static int do_help(int argc, char **argv)
" lru_percpu_hash | lpm_trie | array_of_maps | 
hash_of_maps |\n"
" devmap | devmap_hash | sockmap | cpumap | 
xskmap | sockhash |\n"
" cgroup_storage | reuseport_sockarray | 
percpu_cgroup_storage |\n"
-   " queue | stack | sk_storage | struct_ops | 
ringbuf | inode_storage }\n"
+   " queue | stack | sk_storage | struct_ops | 
ringbuf | inode_storage |\n"
+   " task_storage }\n"
"   " HELP_SPEC_OPTIONS "\n"
"",
bin_name, argv[-2]);
-- 
2.29.1.341.ge80a0c044ae-goog



Re: [PATCH bpf-next v5 8/9] bpf: Add tests for task_local_storage

2020-11-06 Thread KP Singh
On Fri, Nov 6, 2020 at 3:14 AM Alexei Starovoitov
 wrote:
>
> On Thu, Nov 05, 2020 at 10:58:26PM +0000, KP Singh wrote:
> > +
> > + ret = copy_file_range(fd_in, NULL, fd_out, NULL, stat.st_size, 0);
>
> centos7 glibc doesn't have it.
>
> /prog_tests/test_local_storage.c:59:8: warning: implicit declaration of 
> function ‘copy_file_range’; did you mean ‘sync_file_range’? 
> [-Wimplicit-function-declaration]
>59 |  ret = copy_file_range(fd_in, NULL, fd_out, NULL, stat.st_size, 0);
>   |^~~
>   |sync_file_range
>   BINARY   test_progs
>   BINARY   test_progs-no_alu32
> ld: test_local_storage.test.o: in function `copy_rm':
> test_local_storage.c:59: undefined reference to `copy_file_range'
>
> Could you use something else or wrap it similar to pidfd_open ?

Sure, I created a wrapper similar to pidfd_open and sent out a v6.


Re: [PATCH bpf-next v9 5/7] bpf: Implement bpf_local_storage for inodes

2020-08-25 Thread KP Singh



On 8/25/20 2:52 AM, Martin KaFai Lau wrote:
> On Sun, Aug 23, 2020 at 06:56:10PM +0200, KP Singh wrote:
>> From: KP Singh 
>>
>> Similar to bpf_local_storage for sockets, add local storage for inodes.
>> The life-cycle of storage is managed with the life-cycle of the inode.
>> i.e. the storage is destroyed along with the owning inode.
>>
>> The BPF LSM allocates an __rcu pointer to the bpf_local_storage in the
>> security blob which are now stackable and can co-exist with other LSMs.
>>
> [ ... ]
> 
>> diff --git a/kernel/bpf/bpf_inode_storage.c b/kernel/bpf/bpf_inode_storage.c
>> new file mode 100644
>> index ..b0b283c224c1
>> --- /dev/null
>> +++ b/kernel/bpf/bpf_inode_storage.c

[...]

>> +
>> +DEFINE_BPF_STORAGE_CACHE(inode_cache);
>> +
>> +static struct bpf_local_storage __rcu **
>> +inode_storage_ptr(void *owner)
>> +{
>> +struct inode *inode = owner;
>> +struct bpf_storage_blob *bsb;
>> +
>> +bsb = bpf_inode(inode);
>> +if (!bsb)
>> +return NULL;
> just noticed this one.  NULL could be returned here.  When will it happen?

This can happen if CONFIG_BPF_LSM is enabled but "bpf" is not in the list of
active LSMs.

> 
>> +return >storage;
>> +}
>> +
>> +static struct bpf_local_storage_data *inode_storage_lookup(struct inode 
>> *inode,
>> +   struct bpf_map *map,
>> +   bool cacheit_lockit)
>> +{

[...]

> path first before calling the bpf_local_storage_update() since
> this case is specific to inode local storage.
> 
> Same for the other bpf_local_storage_update() cases.

If you're okay with this I can send a new series with the following updates.

diff --git a/kernel/bpf/bpf_inode_storage.c b/kernel/bpf/bpf_inode_storage.c
index b0b283c224c1..74546cee814d 100644
--- a/kernel/bpf/bpf_inode_storage.c
+++ b/kernel/bpf/bpf_inode_storage.c
@@ -125,7 +125,7 @@ static int bpf_fd_inode_storage_update_elem(struct bpf_map 
*map, void *key,
 
fd = *(int *)key;
f = fget_raw(fd);
-   if (!f)
+   if (!f || !inode_storage_ptr(f->f_inode))
return -EBADF;
 
sdata = bpf_local_storage_update(f->f_inode,
@@ -171,6 +171,14 @@ BPF_CALL_4(bpf_inode_storage_get, struct bpf_map *, map, 
struct inode *, inode,
if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE))
return (unsigned long)NULL;
 
+   /* explicitly check that the inode_storage_ptr is not
+* NULL as inode_storage_lookup returns NULL in this case and
+* and bpf_local_storage_update expects the owner to have a
+* valid storage pointer.
+*/
+   if (!inode_storage_ptr(inode))
+   return (unsigned long)NULL;
+
sdata = inode_storage_lookup(inode, map, true);
if (sdata)
return (unsigned long)sdata->data;


> 
>> + (struct bpf_local_storage_map *)map,
>> + value, map_flags);
>> +fput(f);
>> +return PTR_ERR_OR_ZERO(sdata);
>> +}
>> +
> 

[...]

>> +return (unsigned long)NULL;
>> +}
>> +
> 
>> diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
>> index 32d32d485451..35f9b19259e5 100644
>> --- a/security/bpf/hooks.c
>> +++ b/security/bpf/hooks.c
>> @@ -3,6 +3,7 @@
>>  /*
>>   * Copyright (C) 2020 Google LLC.
>>   */
>> +#include 
> Is it needed?

No. Removed. Thanks!

> 
>>  #include 
>>  #include 
>>  
>> @@ -11,6 +12,7 @@ static struct security_hook_list bpf_lsm_hooks[] 
>> __lsm_ro_after_init = {
>>  LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),

[...]

>> +.blobs = _lsm_blob_sizes
>>  };


[PATCH bpf-next v10 0/7] Generalizing bpf_local_storage

2020-08-25 Thread KP Singh
From: KP Singh 

# v9 -> v10

- Added NULL check for inode_storage_ptr before calling
  bpf_local_storage_update
- Removed an extraneous include
- Rebased and added Acks / Signoff.


# v8 -> v9

- Fixed reference count logic for files for inode maps.
- Other fixes suggested by Martin
- Rebase

# v7 -> v8

- Fixed an issue with BTF IDs for helpers and added
  bpf_<>_storage_delete to selftests to catch this issue.
- Update comments about refcounts and grabbed a refcount to the open
  file for userspace inode helpers.
- Rebase.

# v6 -> v7

- Updated the series to use Martin's POC patch:

  https://lore.kernel.org/bpf/20200725013047.4006241-1-ka...@fb.com/

  I added a Co-developed-by: tag, but would need Martin's Signoff
  (was not sure of the procedure here).

- Rebase.

# v5 -> v6

- Fixed a build warning.
- Rebase.

# v4 -> v5

- Split non-functional changes into separate commits.
- Updated the cache macros to be simpler.
- Fixed some bugs noticed by Martin.
- Updated the userspace map functions to use an fd for lookups, updates
  and deletes.
- Rebase.

# v3 -> v4

- Fixed a missing include to bpf_sk_storage.h in bpf_sk_storage.c
- Fixed some functions that were not marked as static which led to
  W=1 compilation warnings.

# v2 -> v3

* Restructured the code as per Martin's suggestions:
  - Common functionality in bpf_local_storage.c
  - bpf_sk_storage functionality remains in net/bpf_sk_storage.
  - bpf_inode_storage is kept separate as it is enabled only with
CONFIG_BPF_LSM.
* A separate cache for inode and sk storage with macros to define it.
* Use the ops style approach as suggested by Martin instead of the
  enum + switch style.
* Added the inode map to bpftool bash completion and docs.
* Rebase and indentation fixes.

# v1 -> v2

* Use the security blob pointer instead of dedicated member in
  struct inode.
* Better code re-use as suggested by Alexei.
* Dropped the inode count arithmetic as pointed out by Alexei.
* Minor bug fixes and rebase.

bpf_sk_storage can already be used by some BPF program types to annotate
socket objects. These annotations are managed with the life-cycle of the
object (i.e. freed when the object is freed) which makes BPF programs
much simpler and less prone to errors and leaks.

This patch series:

* Generalizes the bpf_sk_storage infrastructure to allow easy
  implementation of local storage for other objects
* Implements local storage for inodes
* Makes both bpf_{sk, inode}_storage available to LSM programs.

Local storage is safe to use in LSM programs as the attachment sites are
limited and the owning object won't be freed, however, this is not the
case for tracing. Usage in tracing is expected to follow a white-list
based approach similar to the d_path helper
(https://lore.kernel.org/bpf/20200506132946.2164578-1-jo...@kernel.org).

Access to local storage would allow LSM programs to implement stateful
detections like detecting the unlink of a running executable from the
examples shared as a part of the KRSI series
https://lore.kernel.org/bpf/20200329004356.27286-1-kpsi...@chromium.org/
and
https://github.com/sinkap/linux-krsi/blob/patch/v1/examples/samples/bpf/lsm_detect_exec_unlink.c

KP Singh (7):
  bpf: Renames in preparation for bpf_local_storage
  bpf: Generalize caching for sk_storage.
  bpf: Generalize bpf_sk_storage
  bpf: Split bpf_local_storage to bpf_sk_storage
  bpf: Implement bpf_local_storage for inodes
  bpf: Allow local storage to be used from LSM programs
  bpf: Add selftests for local_storage

 include/linux/bpf.h   |   8 +
 include/linux/bpf_local_storage.h | 163 
 include/linux/bpf_lsm.h   |  29 +
 include/linux/bpf_types.h |   3 +
 include/net/bpf_sk_storage.h  |  14 +
 include/net/sock.h|   4 +-
 include/uapi/linux/bpf.h  |  55 +-
 kernel/bpf/Makefile   |   2 +
 kernel/bpf/bpf_inode_storage.c| 273 ++
 kernel/bpf/bpf_local_storage.c| 600 +
 kernel/bpf/bpf_lsm.c  |  21 +-
 kernel/bpf/syscall.c  |   3 +-
 kernel/bpf/verifier.c |  10 +
 net/core/bpf_sk_storage.c | 830 +++---
 security/bpf/hooks.c  |   6 +
 .../bpf/bpftool/Documentation/bpftool-map.rst |   2 +-
 tools/bpf/bpftool/bash-completion/bpftool |   3 +-
 tools/bpf/bpftool/map.c   |   3 +-
 tools/include/uapi/linux/bpf.h|  55 +-
 tools/lib/bpf/libbpf_probes.c |   5 +-
 .../bpf/prog_tests/test_local_storage.c   |  60 ++
 .../selftests/bpf/progs/local_storage.c   | 140 +++
 22 files changed, 1575 insertions(+), 714 deletions(-)
 create mode 100644 include/linux/bpf_local_storage.h
 create mode 100644 kernel/bpf/bpf_inode_storage.c
 create mode 100644 kerne

[PATCH bpf-next v10 5/7] bpf: Implement bpf_local_storage for inodes

2020-08-25 Thread KP Singh
From: KP Singh 

Similar to bpf_local_storage for sockets, add local storage for inodes.
The life-cycle of storage is managed with the life-cycle of the inode.
i.e. the storage is destroyed along with the owning inode.

The BPF LSM allocates an __rcu pointer to the bpf_local_storage in the
security blob which are now stackable and can co-exist with other LSMs.

Signed-off-by: KP Singh 
---
 include/linux/bpf_lsm.h   |  29 ++
 include/linux/bpf_types.h |   3 +
 include/uapi/linux/bpf.h  |  40 ++-
 kernel/bpf/Makefile   |   1 +
 kernel/bpf/bpf_inode_storage.c| 273 ++
 kernel/bpf/syscall.c  |   3 +-
 kernel/bpf/verifier.c |  10 +
 security/bpf/hooks.c  |   6 +
 .../bpf/bpftool/Documentation/bpftool-map.rst |   2 +-
 tools/bpf/bpftool/bash-completion/bpftool |   3 +-
 tools/bpf/bpftool/map.c   |   3 +-
 tools/include/uapi/linux/bpf.h|  40 ++-
 tools/lib/bpf/libbpf_probes.c |   5 +-
 13 files changed, 410 insertions(+), 8 deletions(-)
 create mode 100644 kernel/bpf/bpf_inode_storage.c

diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index af74712af585..aaacb6aafc87 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -17,9 +17,28 @@
 #include 
 #undef LSM_HOOK
 
+struct bpf_storage_blob {
+   struct bpf_local_storage __rcu *storage;
+};
+
+extern struct lsm_blob_sizes bpf_lsm_blob_sizes;
+
 int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
const struct bpf_prog *prog);
 
+static inline struct bpf_storage_blob *bpf_inode(
+   const struct inode *inode)
+{
+   if (unlikely(!inode->i_security))
+   return NULL;
+
+   return inode->i_security + bpf_lsm_blob_sizes.lbs_inode;
+}
+
+extern const struct bpf_func_proto bpf_inode_storage_get_proto;
+extern const struct bpf_func_proto bpf_inode_storage_delete_proto;
+void bpf_inode_storage_free(struct inode *inode);
+
 #else /* !CONFIG_BPF_LSM */
 
 static inline int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
@@ -28,6 +47,16 @@ static inline int bpf_lsm_verify_prog(struct 
bpf_verifier_log *vlog,
return -EOPNOTSUPP;
 }
 
+static inline struct bpf_storage_blob *bpf_inode(
+   const struct inode *inode)
+{
+   return NULL;
+}
+
+static inline void bpf_inode_storage_free(struct inode *inode)
+{
+}
+
 #endif /* CONFIG_BPF_LSM */
 
 #endif /* _LINUX_BPF_LSM_H */
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index a52a5688418e..2e6f568377f1 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -107,6 +107,9 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_SK_STORAGE, sk_storage_map_ops)
 BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKMAP, sock_map_ops)
 BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKHASH, sock_hash_ops)
 #endif
+#ifdef CONFIG_BPF_LSM
+BPF_MAP_TYPE(BPF_MAP_TYPE_INODE_STORAGE, inode_storage_map_ops)
+#endif
 BPF_MAP_TYPE(BPF_MAP_TYPE_CPUMAP, cpu_map_ops)
 #if defined(CONFIG_XDP_SOCKETS)
 BPF_MAP_TYPE(BPF_MAP_TYPE_XSKMAP, xsk_map_ops)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 2cbd137eed86..b6bfcd085a76 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -155,6 +155,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_DEVMAP_HASH,
BPF_MAP_TYPE_STRUCT_OPS,
BPF_MAP_TYPE_RINGBUF,
+   BPF_MAP_TYPE_INODE_STORAGE,
 };
 
 /* Note that tracing related programs such as
@@ -3509,6 +3510,41 @@ union bpf_attr {
  *
  * **-EPERM** This helper cannot be used under the
  *current sock_ops->op.
+ * void *bpf_inode_storage_get(struct bpf_map *map, void *inode, void *value, 
u64 flags)
+ * Description
+ * Get a bpf_local_storage from an *inode*.
+ *
+ * Logically, it could be thought of as getting the value from
+ * a *map* with *inode* as the **key**.  From this
+ * perspective,  the usage is not much different from
+ * **bpf_map_lookup_elem**\ (*map*, **&**\ *inode*) except this
+ * helper enforces the key must be an inode and the map must also
+ * be a **BPF_MAP_TYPE_INODE_STORAGE**.
+ *
+ * Underneath, the value is stored locally at *inode* instead of
+ * the *map*.  The *map* is used as the bpf-local-storage
+ * "type". The bpf-local-storage "type" (i.e. the *map*) is
+ * searched against all bpf_local_storage residing at *inode*.
+ *
+ * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be
+ * used such that a new bpf_local_storage will be
+ * created if one does not exist.  *value* can be used
+ * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify
+ * the initial value of a bpf_local_storage.  If 

[PATCH bpf-next v10 3/7] bpf: Generalize bpf_sk_storage

2020-08-25 Thread KP Singh
From: KP Singh 

Refactor the functionality in bpf_sk_storage.c so that concept of
storage linked to kernel objects can be extended to other objects like
inode, task_struct etc.

Each new local storage will still be a separate map and provide its own
set of helpers. This allows for future object specific extensions and
still share a lot of the underlying implementation.

This includes the changes suggested by Martin in:

  https://lore.kernel.org/bpf/20200725013047.4006241-1-ka...@fb.com/

adding new map operations to support bpf_local_storage maps:

* storages for different kernel objects to optionally have different
  memory charging strategy (map_local_storage_charge,
  map_local_storage_uncharge)
* Functionality to extract the storage pointer from a pointer to the
  owning object (map_owner_storage_ptr)

Co-developed-by: Martin KaFai Lau 
Signed-off-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 include/linux/bpf.h|   8 ++
 include/net/bpf_sk_storage.h   |  52 +++
 include/uapi/linux/bpf.h   |   8 +-
 net/core/bpf_sk_storage.c  | 238 +
 tools/include/uapi/linux/bpf.h |   8 +-
 5 files changed, 228 insertions(+), 86 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 81f38e2fda78..8c443b93ac11 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -34,6 +34,8 @@ struct btf_type;
 struct exception_table_entry;
 struct seq_operations;
 struct bpf_iter_aux_info;
+struct bpf_local_storage;
+struct bpf_local_storage_map;
 
 extern struct idr btf_idr;
 extern spinlock_t btf_idr_lock;
@@ -104,6 +106,12 @@ struct bpf_map_ops {
__poll_t (*map_poll)(struct bpf_map *map, struct file *filp,
 struct poll_table_struct *pts);
 
+   /* Functions called by bpf_local_storage maps */
+   int (*map_local_storage_charge)(struct bpf_local_storage_map *smap,
+   void *owner, u32 size);
+   void (*map_local_storage_uncharge)(struct bpf_local_storage_map *smap,
+  void *owner, u32 size);
+   struct bpf_local_storage __rcu ** (*map_owner_storage_ptr)(void *owner);
/* BTF name and id of struct allocated by map_alloc */
const char * const map_btf_name;
int *map_btf_id;
diff --git a/include/net/bpf_sk_storage.h b/include/net/bpf_sk_storage.h
index 950c5aaba15e..9e631b5466e3 100644
--- a/include/net/bpf_sk_storage.h
+++ b/include/net/bpf_sk_storage.h
@@ -3,8 +3,15 @@
 #ifndef _BPF_SK_STORAGE_H
 #define _BPF_SK_STORAGE_H
 
+#include 
+#include 
+#include 
 #include 
 #include 
+#include 
+#include 
+#include 
+#include 
 
 struct sock;
 
@@ -13,6 +20,7 @@ void bpf_sk_storage_free(struct sock *sk);
 extern const struct bpf_func_proto bpf_sk_storage_get_proto;
 extern const struct bpf_func_proto bpf_sk_storage_delete_proto;
 
+struct bpf_local_storage_elem;
 struct bpf_sk_storage_diag;
 struct sk_buff;
 struct nlattr;
@@ -34,6 +42,50 @@ u16 bpf_local_storage_cache_idx_get(struct 
bpf_local_storage_cache *cache);
 void bpf_local_storage_cache_idx_free(struct bpf_local_storage_cache *cache,
  u16 idx);
 
+/* Helper functions for bpf_local_storage */
+int bpf_local_storage_map_alloc_check(union bpf_attr *attr);
+
+struct bpf_local_storage_map *bpf_local_storage_map_alloc(union bpf_attr 
*attr);
+
+struct bpf_local_storage_data *
+bpf_local_storage_lookup(struct bpf_local_storage *local_storage,
+struct bpf_local_storage_map *smap,
+bool cacheit_lockit);
+
+void bpf_local_storage_map_free(struct bpf_local_storage_map *smap);
+
+int bpf_local_storage_map_check_btf(const struct bpf_map *map,
+   const struct btf *btf,
+   const struct btf_type *key_type,
+   const struct btf_type *value_type);
+
+void bpf_selem_link_storage_nolock(struct bpf_local_storage *local_storage,
+  struct bpf_local_storage_elem *selem);
+
+bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage,
+struct bpf_local_storage_elem *selem,
+bool uncharge_omem);
+
+void bpf_selem_unlink(struct bpf_local_storage_elem *selem);
+
+void bpf_selem_link_map(struct bpf_local_storage_map *smap,
+   struct bpf_local_storage_elem *selem);
+
+void bpf_selem_unlink_map(struct bpf_local_storage_elem *selem);
+
+struct bpf_local_storage_elem *
+bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner, void *value,
+   bool charge_mem);
+
+int
+bpf_local_storage_alloc(void *owner,
+   struct bpf_local_storage_map *smap,
+   struct bpf_local_storage_elem *first_selem);
+
+struct bpf_local_storage_data *
+bpf_local_storage_update(void *owner, struct bpf_local_storage_map

[PATCH bpf-next v10 6/7] bpf: Allow local storage to be used from LSM programs

2020-08-25 Thread KP Singh
From: KP Singh 

Adds support for both bpf_{sk, inode}_storage_{get, delete} to be used
in LSM programs. These helpers are not used for tracing programs
(currently) as their usage is tied to the life-cycle of the object and
should only be used where the owning object won't be freed (when the
owning object is passed as an argument to the LSM hook). Thus, they
are safer to use in LSM hooks than tracing. Usage of local storage in
tracing programs will probably follow a per function based whitelist
approach.

Since the UAPI helper signature for bpf_sk_storage expect a bpf_sock,
it, leads to a compilation warning for LSM programs, it's also updated
to accept a void * pointer instead.

Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 include/net/bpf_sk_storage.h   |  2 ++
 include/uapi/linux/bpf.h   |  7 +--
 kernel/bpf/bpf_lsm.c   | 21 -
 net/core/bpf_sk_storage.c  | 25 +
 tools/include/uapi/linux/bpf.h |  7 +--
 5 files changed, 57 insertions(+), 5 deletions(-)

diff --git a/include/net/bpf_sk_storage.h b/include/net/bpf_sk_storage.h
index 3c516dd07caf..119f4c9c3a9c 100644
--- a/include/net/bpf_sk_storage.h
+++ b/include/net/bpf_sk_storage.h
@@ -20,6 +20,8 @@ void bpf_sk_storage_free(struct sock *sk);
 
 extern const struct bpf_func_proto bpf_sk_storage_get_proto;
 extern const struct bpf_func_proto bpf_sk_storage_delete_proto;
+extern const struct bpf_func_proto sk_storage_get_btf_proto;
+extern const struct bpf_func_proto sk_storage_delete_btf_proto;
 
 struct bpf_local_storage_elem;
 struct bpf_sk_storage_diag;
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index b6bfcd085a76..0e1cdf806fe1 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -2808,7 +2808,7 @@ union bpf_attr {
  *
  * **-ERANGE** if resulting value was out of range.
  *
- * void *bpf_sk_storage_get(struct bpf_map *map, struct bpf_sock *sk, void 
*value, u64 flags)
+ * void *bpf_sk_storage_get(struct bpf_map *map, void *sk, void *value, u64 
flags)
  * Description
  * Get a bpf-local-storage from a *sk*.
  *
@@ -2824,6 +2824,9 @@ union bpf_attr {
  * "type". The bpf-local-storage "type" (i.e. the *map*) is
  * searched against all bpf-local-storages residing at *sk*.
  *
+ * *sk* is a kernel **struct sock** pointer for LSM program.
+ * *sk* is a **struct bpf_sock** pointer for other program types.
+ *
  * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be
  * used such that a new bpf-local-storage will be
  * created if one does not exist.  *value* can be used
@@ -2836,7 +2839,7 @@ union bpf_attr {
  * **NULL** if not found or there was an error in adding
  * a new bpf-local-storage.
  *
- * long bpf_sk_storage_delete(struct bpf_map *map, struct bpf_sock *sk)
+ * long bpf_sk_storage_delete(struct bpf_map *map, void *sk)
  * Description
  * Delete a bpf-local-storage from a *sk*.
  * Return
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index fb278144e9fd..9cd1428c7199 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -11,6 +11,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 /* For every LSM hook that allows attachment of BPF programs, declare a nop
  * function where a BPF program can be attached.
@@ -45,10 +47,27 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
return 0;
 }
 
+static const struct bpf_func_proto *
+bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+{
+   switch (func_id) {
+   case BPF_FUNC_inode_storage_get:
+   return _inode_storage_get_proto;
+   case BPF_FUNC_inode_storage_delete:
+   return _inode_storage_delete_proto;
+   case BPF_FUNC_sk_storage_get:
+   return _storage_get_btf_proto;
+   case BPF_FUNC_sk_storage_delete:
+   return _storage_delete_btf_proto;
+   default:
+   return tracing_prog_func_proto(func_id, prog);
+   }
+}
+
 const struct bpf_prog_ops lsm_prog_ops = {
 };
 
 const struct bpf_verifier_ops lsm_verifier_ops = {
-   .get_func_proto = tracing_prog_func_proto,
+   .get_func_proto = bpf_lsm_func_proto,
.is_valid_access = btf_ctx_access,
 };
diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c
index f29d9a9b4ea4..55fae03b4cc3 100644
--- a/net/core/bpf_sk_storage.c
+++ b/net/core/bpf_sk_storage.c
@@ -12,6 +12,7 @@
 #include 
 #include 
 #include 
+#include 
 
 DEFINE_BPF_STORAGE_CACHE(sk_cache);
 
@@ -377,6 +378,30 @@ const struct bpf_func_proto bpf_sk_storage_delete_proto = {
.arg2_type  = ARG_PTR_TO_SOCKET,
 };
 
+BTF_ID_LIST(sk_storage_btf_ids)
+BTF_ID_UNUSED
+BTF_ID(struct, sock)
+
+const struct bpf_func_proto sk_storage_get_btf_proto = {
+   .func   = bpf_sk_storage_get,
+   .gp

[PATCH bpf-next v10 1/7] bpf: Renames in preparation for bpf_local_storage

2020-08-25 Thread KP Singh
From: KP Singh 

A purely mechanical change to split the renaming from the actual
generalization.

Flags/consts:

  SK_STORAGE_CREATE_FLAG_MASK   BPF_LOCAL_STORAGE_CREATE_FLAG_MASK
  BPF_SK_STORAGE_CACHE_SIZE BPF_LOCAL_STORAGE_CACHE_SIZE
  MAX_VALUE_SIZEBPF_LOCAL_STORAGE_MAX_VALUE_SIZE

Structs:

  bucketbpf_local_storage_map_bucket
  bpf_sk_storage_mapbpf_local_storage_map
  bpf_sk_storage_data   bpf_local_storage_data
  bpf_sk_storage_elem   bpf_local_storage_elem
  bpf_sk_storagebpf_local_storage

The "sk" member in bpf_local_storage is also updated to "owner"
in preparation for changing the type to void * in a subsequent patch.

Functions:

  selem_linked_to_skselem_linked_to_storage
  selem_alloc   bpf_selem_alloc
  __selem_unlink_sk bpf_selem_unlink_storage_nolock
  __selem_link_sk   bpf_selem_link_storage_nolock
  selem_unlink_sk   __bpf_selem_unlink_storage
  sk_storage_update bpf_local_storage_update
  __sk_storage_lookup   bpf_local_storage_lookup
  bpf_sk_storage_map_free   bpf_local_storage_map_free
  bpf_sk_storage_map_alloc  bpf_local_storage_map_alloc
  bpf_sk_storage_map_alloc_checkbpf_local_storage_map_alloc_check
  bpf_sk_storage_map_check_btf  bpf_local_storage_map_check_btf

Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 include/net/sock.h|   4 +-
 net/core/bpf_sk_storage.c | 488 +++---
 2 files changed, 252 insertions(+), 240 deletions(-)

diff --git a/include/net/sock.h b/include/net/sock.h
index 064637d1ddf6..18423cc9cde8 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -246,7 +246,7 @@ struct sock_common {
/* public: */
 };
 
-struct bpf_sk_storage;
+struct bpf_local_storage;
 
 /**
   *struct sock - network layer representation of sockets
@@ -517,7 +517,7 @@ struct sock {
void(*sk_destruct)(struct sock *sk);
struct sock_reuseport __rcu *sk_reuseport_cb;
 #ifdef CONFIG_BPF_SYSCALL
-   struct bpf_sk_storage __rcu *sk_bpf_storage;
+   struct bpf_local_storage __rcu  *sk_bpf_storage;
 #endif
struct rcu_head sk_rcu;
 };
diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c
index 281200dc0a01..f975e2d01207 100644
--- a/net/core/bpf_sk_storage.c
+++ b/net/core/bpf_sk_storage.c
@@ -12,33 +12,32 @@
 #include 
 #include 
 
-#define SK_STORAGE_CREATE_FLAG_MASK\
-   (BPF_F_NO_PREALLOC | BPF_F_CLONE)
+#define BPF_LOCAL_STORAGE_CREATE_FLAG_MASK (BPF_F_NO_PREALLOC | BPF_F_CLONE)
 
-struct bucket {
+struct bpf_local_storage_map_bucket {
struct hlist_head list;
raw_spinlock_t lock;
 };
 
-/* Thp map is not the primary owner of a bpf_sk_storage_elem.
- * Instead, the sk->sk_bpf_storage is.
+/* Thp map is not the primary owner of a bpf_local_storage_elem.
+ * Instead, the container object (eg. sk->sk_bpf_storage) is.
  *
- * The map (bpf_sk_storage_map) is for two purposes
- * 1. Define the size of the "sk local storage".  It is
+ * The map (bpf_local_storage_map) is for two purposes
+ * 1. Define the size of the "local storage".  It is
  *the map's value_size.
  *
  * 2. Maintain a list to keep track of all elems such
  *that they can be cleaned up during the map destruction.
  *
  * When a bpf local storage is being looked up for a
- * particular sk,  the "bpf_map" pointer is actually used
+ * particular object,  the "bpf_map" pointer is actually used
  * as the "key" to search in the list of elem in
- * sk->sk_bpf_storage.
+ * the respective bpf_local_storage owned by the object.
  *
- * Hence, consider sk->sk_bpf_storage is the mini-map
- * with the "bpf_map" pointer as the searching key.
+ * e.g. sk->sk_bpf_storage is the mini-map with the "bpf_map" pointer
+ * as the searching key.
  */
-struct bpf_sk_storage_map {
+struct bpf_local_storage_map {
struct bpf_map map;
/* Lookup elem does not require accessing the map.
 *
@@ -46,55 +45,57 @@ struct bpf_sk_storage_map {
 * link/unlink the elem from the map.  Having
 * multiple buckets to improve contention.
 */
-   struct bucket *buckets;
+   struct bpf_local_storage_map_bucket *buckets;
u32 bucket_log;
u16 elem_size;
u16 cache_idx;
 };
 
-struct bpf_sk_storage_data {
+struct bpf_local_storage_data {
/* smap is used as the searching key when looking up
-* from sk->sk_bpf_storage.
+* from the object's bpf_local_storage.
 *
 * Put it in the same cacheline as the data to minimize
 * the number of cachelines access during the cache hit case.

[PATCH bpf-next v10 7/7] bpf: Add selftests for local_storage

2020-08-25 Thread KP Singh
From: KP Singh 

inode_local_storage:

* Hook to the file_open and inode_unlink LSM hooks.
* Create and unlink a temporary file.
* Store some information in the inode's bpf_local_storage during
  file_open.
* Verify that this information exists when the file is unlinked.

sk_local_storage:

* Hook to the socket_post_create and socket_bind LSM hooks.
* Open and bind a socket and set the sk_storage in the
  socket_post_create hook using the start_server helper.
* Verify if the information is set in the socket_bind hook.

Acked-by: Andrii Nakryiko 
Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c   |  60 
 .../selftests/bpf/progs/local_storage.c   | 140 ++
 2 files changed, 200 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/test_local_storage.c
 create mode 100644 tools/testing/selftests/bpf/progs/local_storage.c

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
new file mode 100644
index ..91cd6f357246
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2020 Google LLC.
+ */
+
+#include 
+#include 
+
+#include "local_storage.skel.h"
+#include "network_helpers.h"
+
+int create_and_unlink_file(void)
+{
+   char fname[PATH_MAX] = "/tmp/fileXX";
+   int fd;
+
+   fd = mkstemp(fname);
+   if (fd < 0)
+   return fd;
+
+   close(fd);
+   unlink(fname);
+   return 0;
+}
+
+void test_test_local_storage(void)
+{
+   struct local_storage *skel = NULL;
+   int err, duration = 0, serv_sk = -1;
+
+   skel = local_storage__open_and_load();
+   if (CHECK(!skel, "skel_load", "lsm skeleton failed\n"))
+   goto close_prog;
+
+   err = local_storage__attach(skel);
+   if (CHECK(err, "attach", "lsm attach failed: %d\n", err))
+   goto close_prog;
+
+   skel->bss->monitored_pid = getpid();
+
+   err = create_and_unlink_file();
+   if (CHECK(err < 0, "exec_cmd", "err %d errno %d\n", err, errno))
+   goto close_prog;
+
+   CHECK(skel->data->inode_storage_result != 0, "inode_storage_result",
+ "inode_local_storage not set\n");
+
+   serv_sk = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
+   if (CHECK(serv_sk < 0, "start_server", "failed to start server\n"))
+   goto close_prog;
+
+   CHECK(skel->data->sk_storage_result != 0, "sk_storage_result",
+ "sk_local_storage not set\n");
+
+   close(serv_sk);
+
+close_prog:
+   local_storage__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
new file mode 100644
index ..0758ba229ae0
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2020 Google LLC.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+char _license[] SEC("license") = "GPL";
+
+#define DUMMY_STORAGE_VALUE 0xdeadbeef
+
+int monitored_pid = 0;
+int inode_storage_result = -1;
+int sk_storage_result = -1;
+
+struct dummy_storage {
+   __u32 value;
+};
+
+struct {
+   __uint(type, BPF_MAP_TYPE_INODE_STORAGE);
+   __uint(map_flags, BPF_F_NO_PREALLOC);
+   __type(key, int);
+   __type(value, struct dummy_storage);
+} inode_storage_map SEC(".maps");
+
+struct {
+   __uint(type, BPF_MAP_TYPE_SK_STORAGE);
+   __uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE);
+   __type(key, int);
+   __type(value, struct dummy_storage);
+} sk_storage_map SEC(".maps");
+
+/* TODO Use vmlinux.h once BTF pruning for embedded types is fixed.
+ */
+struct sock {} __attribute__((preserve_access_index));
+struct sockaddr {} __attribute__((preserve_access_index));
+struct socket {
+   struct sock *sk;
+} __attribute__((preserve_access_index));
+
+struct inode {} __attribute__((preserve_access_index));
+struct dentry {
+   struct inode *d_inode;
+} __attribute__((preserve_access_index));
+struct file {
+   struct inode *f_inode;
+} __attribute__((preserve_access_index));
+
+
+SEC("lsm/inode_unlink")
+int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
+{
+   __u32 pid = bpf_get_current_pid_tgid() >> 32;
+   struct dummy_storage *storage;
+
+   if (pid != monitored_pid)
+   return 0;
+
+   storage = bpf_inode_storage_get(_storage_map, victim->d_inode, 0,
+BPF_SK_STORAGE_GET_F_CREATE);
+   if (!storage)
+   return 0;
+
+   if

[PATCH bpf-next v10 2/7] bpf: Generalize caching for sk_storage.

2020-08-25 Thread KP Singh
From: KP Singh 

Provide the a ability to define local storage caches on a per-object
type basis. The caches and caching indices for different objects should
not be inter-mixed as suggested in:

  
https://lore.kernel.org/bpf/20200630193441.kdwnkestulg5e...@kafai-mbp.dhcp.thefacebook.com/

  "Caching a sk-storage at idx=0 of a sk should not stop an
  inode-storage to be cached at the same idx of a inode."

Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 include/net/bpf_sk_storage.h | 19 +++
 net/core/bpf_sk_storage.c| 31 +++
 2 files changed, 34 insertions(+), 16 deletions(-)

diff --git a/include/net/bpf_sk_storage.h b/include/net/bpf_sk_storage.h
index 5036c94c0503..950c5aaba15e 100644
--- a/include/net/bpf_sk_storage.h
+++ b/include/net/bpf_sk_storage.h
@@ -3,6 +3,9 @@
 #ifndef _BPF_SK_STORAGE_H
 #define _BPF_SK_STORAGE_H
 
+#include 
+#include 
+
 struct sock;
 
 void bpf_sk_storage_free(struct sock *sk);
@@ -15,6 +18,22 @@ struct sk_buff;
 struct nlattr;
 struct sock;
 
+#define BPF_LOCAL_STORAGE_CACHE_SIZE   16
+
+struct bpf_local_storage_cache {
+   spinlock_t idx_lock;
+   u64 idx_usage_counts[BPF_LOCAL_STORAGE_CACHE_SIZE];
+};
+
+#define DEFINE_BPF_STORAGE_CACHE(name) \
+static struct bpf_local_storage_cache name = { \
+   .idx_lock = __SPIN_LOCK_UNLOCKED(name.idx_lock),\
+}
+
+u16 bpf_local_storage_cache_idx_get(struct bpf_local_storage_cache *cache);
+void bpf_local_storage_cache_idx_free(struct bpf_local_storage_cache *cache,
+ u16 idx);
+
 #ifdef CONFIG_BPF_SYSCALL
 int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk);
 struct bpf_sk_storage_diag *
diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c
index f975e2d01207..ec61ee7c7ee4 100644
--- a/net/core/bpf_sk_storage.c
+++ b/net/core/bpf_sk_storage.c
@@ -14,6 +14,8 @@
 
 #define BPF_LOCAL_STORAGE_CREATE_FLAG_MASK (BPF_F_NO_PREALLOC | BPF_F_CLONE)
 
+DEFINE_BPF_STORAGE_CACHE(sk_cache);
+
 struct bpf_local_storage_map_bucket {
struct hlist_head list;
raw_spinlock_t lock;
@@ -78,10 +80,6 @@ struct bpf_local_storage_elem {
 #define SELEM(_SDATA)  \
container_of((_SDATA), struct bpf_local_storage_elem, sdata)
 #define SDATA(_SELEM) (&(_SELEM)->sdata)
-#define BPF_LOCAL_STORAGE_CACHE_SIZE   16
-
-static DEFINE_SPINLOCK(cache_idx_lock);
-static u64 cache_idx_usage_counts[BPF_LOCAL_STORAGE_CACHE_SIZE];
 
 struct bpf_local_storage {
struct bpf_local_storage_data __rcu 
*cache[BPF_LOCAL_STORAGE_CACHE_SIZE];
@@ -521,16 +519,16 @@ static int sk_storage_delete(struct sock *sk, struct 
bpf_map *map)
return 0;
 }
 
-static u16 cache_idx_get(void)
+u16 bpf_local_storage_cache_idx_get(struct bpf_local_storage_cache *cache)
 {
u64 min_usage = U64_MAX;
u16 i, res = 0;
 
-   spin_lock(_idx_lock);
+   spin_lock(>idx_lock);
 
for (i = 0; i < BPF_LOCAL_STORAGE_CACHE_SIZE; i++) {
-   if (cache_idx_usage_counts[i] < min_usage) {
-   min_usage = cache_idx_usage_counts[i];
+   if (cache->idx_usage_counts[i] < min_usage) {
+   min_usage = cache->idx_usage_counts[i];
res = i;
 
/* Found a free cache_idx */
@@ -538,18 +536,19 @@ static u16 cache_idx_get(void)
break;
}
}
-   cache_idx_usage_counts[res]++;
+   cache->idx_usage_counts[res]++;
 
-   spin_unlock(_idx_lock);
+   spin_unlock(>idx_lock);
 
return res;
 }
 
-static void cache_idx_free(u16 idx)
+void bpf_local_storage_cache_idx_free(struct bpf_local_storage_cache *cache,
+ u16 idx)
 {
-   spin_lock(_idx_lock);
-   cache_idx_usage_counts[idx]--;
-   spin_unlock(_idx_lock);
+   spin_lock(>idx_lock);
+   cache->idx_usage_counts[idx]--;
+   spin_unlock(>idx_lock);
 }
 
 /* Called by __sk_destruct() & bpf_sk_storage_clone() */
@@ -601,7 +600,7 @@ static void bpf_local_storage_map_free(struct bpf_map *map)
 
smap = (struct bpf_local_storage_map *)map;
 
-   cache_idx_free(smap->cache_idx);
+   bpf_local_storage_cache_idx_free(_cache, smap->cache_idx);
 
/* Note that this map might be concurrently cloned from
 * bpf_sk_storage_clone. Wait for any existing bpf_sk_storage_clone
@@ -718,7 +717,7 @@ static struct bpf_map *bpf_local_storage_map_alloc(union 
bpf_attr *attr)
 
smap->elem_size =
sizeof(struct bpf_local_storage_elem) + attr->value_size;
-   smap->cache_idx = cache_idx_get();
+   smap->cache_idx = bpf_local_storage_cache_idx_get(_cache);
 
return >map;
 }
-- 
2.28.0.297.g1956fa8f8d-goog



[PATCH bpf-next v10 4/7] bpf: Split bpf_local_storage to bpf_sk_storage

2020-08-25 Thread KP Singh
From: KP Singh 

A purely mechanical change:

bpf_sk_storage.c = bpf_sk_storage.c + bpf_local_storage.c
bpf_sk_storage.h = bpf_sk_storage.h + bpf_local_storage.h

Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 include/linux/bpf_local_storage.h | 163 
 include/net/bpf_sk_storage.h  |  61 +--
 kernel/bpf/Makefile   |   1 +
 kernel/bpf/bpf_local_storage.c| 600 ++
 net/core/bpf_sk_storage.c | 672 +-
 5 files changed, 766 insertions(+), 731 deletions(-)
 create mode 100644 include/linux/bpf_local_storage.h
 create mode 100644 kernel/bpf/bpf_local_storage.c

diff --git a/include/linux/bpf_local_storage.h 
b/include/linux/bpf_local_storage.h
new file mode 100644
index ..b2c9463f36a1
--- /dev/null
+++ b/include/linux/bpf_local_storage.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 Facebook
+ * Copyright 2020 Google LLC.
+ */
+
+#ifndef _BPF_LOCAL_STORAGE_H
+#define _BPF_LOCAL_STORAGE_H
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define BPF_LOCAL_STORAGE_CACHE_SIZE   16
+
+struct bpf_local_storage_map_bucket {
+   struct hlist_head list;
+   raw_spinlock_t lock;
+};
+
+/* Thp map is not the primary owner of a bpf_local_storage_elem.
+ * Instead, the container object (eg. sk->sk_bpf_storage) is.
+ *
+ * The map (bpf_local_storage_map) is for two purposes
+ * 1. Define the size of the "local storage".  It is
+ *the map's value_size.
+ *
+ * 2. Maintain a list to keep track of all elems such
+ *that they can be cleaned up during the map destruction.
+ *
+ * When a bpf local storage is being looked up for a
+ * particular object,  the "bpf_map" pointer is actually used
+ * as the "key" to search in the list of elem in
+ * the respective bpf_local_storage owned by the object.
+ *
+ * e.g. sk->sk_bpf_storage is the mini-map with the "bpf_map" pointer
+ * as the searching key.
+ */
+struct bpf_local_storage_map {
+   struct bpf_map map;
+   /* Lookup elem does not require accessing the map.
+*
+* Updating/Deleting requires a bucket lock to
+* link/unlink the elem from the map.  Having
+* multiple buckets to improve contention.
+*/
+   struct bpf_local_storage_map_bucket *buckets;
+   u32 bucket_log;
+   u16 elem_size;
+   u16 cache_idx;
+};
+
+struct bpf_local_storage_data {
+   /* smap is used as the searching key when looking up
+* from the object's bpf_local_storage.
+*
+* Put it in the same cacheline as the data to minimize
+* the number of cachelines access during the cache hit case.
+*/
+   struct bpf_local_storage_map __rcu *smap;
+   u8 data[] __aligned(8);
+};
+
+/* Linked to bpf_local_storage and bpf_local_storage_map */
+struct bpf_local_storage_elem {
+   struct hlist_node map_node; /* Linked to bpf_local_storage_map */
+   struct hlist_node snode;/* Linked to bpf_local_storage */
+   struct bpf_local_storage __rcu *local_storage;
+   struct rcu_head rcu;
+   /* 8 bytes hole */
+   /* The data is stored in aother cacheline to minimize
+* the number of cachelines access during a cache hit.
+*/
+   struct bpf_local_storage_data sdata cacheline_aligned;
+};
+
+struct bpf_local_storage {
+   struct bpf_local_storage_data __rcu 
*cache[BPF_LOCAL_STORAGE_CACHE_SIZE];
+   struct hlist_head list; /* List of bpf_local_storage_elem */
+   void *owner;/* The object that owns the above "list" of
+* bpf_local_storage_elem.
+*/
+   struct rcu_head rcu;
+   raw_spinlock_t lock;/* Protect adding/removing from the "list" */
+};
+
+/* U16_MAX is much more than enough for sk local storage
+ * considering a tcp_sock is ~2k.
+ */
+#define BPF_LOCAL_STORAGE_MAX_VALUE_SIZE  \
+   min_t(u32, \
+ (KMALLOC_MAX_SIZE - MAX_BPF_STACK -  \
+  sizeof(struct bpf_local_storage_elem)), \
+ (U16_MAX - sizeof(struct bpf_local_storage_elem)))
+
+#define SELEM(_SDATA)  
\
+   container_of((_SDATA), struct bpf_local_storage_elem, sdata)
+#define SDATA(_SELEM) (&(_SELEM)->sdata)
+
+#define BPF_LOCAL_STORAGE_CACHE_SIZE   16
+
+struct bpf_local_storage_cache {
+   spinlock_t idx_lock;
+   u64 idx_usage_counts[BPF_LOCAL_STORAGE_CACHE_SIZE];
+};
+
+#define DEFINE_BPF_STORAGE_CACHE(name) \
+static struct bpf_local_storage_cache name = { \
+   .idx_lock = __SPIN_LOCK_UNLOCKED(name.idx_lock),\
+}
+
+

Re: [PATCH bpf-next v10 0/7] Generalizing bpf_local_storage

2020-08-25 Thread KP Singh
On Wed, Aug 26, 2020 at 12:13 AM Alexei Starovoitov
 wrote:
>
> On Tue, Aug 25, 2020 at 2:05 PM Alexei Starovoitov
>  wrote:
> >
> > On Tue, Aug 25, 2020 at 11:29 AM KP Singh  wrote:
> > >
> > > From: KP Singh 
> > >
> > > # v9 -> v10
> > >
> > > - Added NULL check for inode_storage_ptr before calling
> > >   bpf_local_storage_update
> > > - Removed an extraneous include
> > > - Rebased and added Acks / Signoff.
> >
> > Hmm. Though it looks good I cannot apply it, because
> > test_progs -t map_ptr
> > is broken:
> > 2225: (18) r2 = 0xc94e5004
> > 2227: (b4) w1 = 58
> > 2228: (63) *(u32 *)(r2 +0) = r1
> >  R0=map_value(id=0,off=0,ks=4,vs=4,imm=0) R1_w=inv58
> > R2_w=map_value(id=0,off=4,ks=4,vs=8,imm=0) R3=inv49 R4=inv63
> > R5=inv(id=0,umax_value=4294967295,var_off=(0x0; 0x)) R6_w=inv0
> > R7=invP8 R8=map_ptr(id=0,off=0,ks=4,vs=4,imm=0) R10=?
> > ; VERIFY_TYPE(BPF_MAP_TYPE_SK_STORAGE, check_sk_storage);
> > 2229: (18) r1 = 0xc94e5000
> > 2231: (b4) w3 = 24
> > 2232: (63) *(u32 *)(r1 +0) = r3
> >  R0=map_value(id=0,off=0,ks=4,vs=4,imm=0)
> > R1_w=map_value(id=0,off=0,ks=4,vs=8,imm=0)
> > R2_w=map_value(id=0,off=4,ks=4,vs=8,imm=0) R3_w=inv24 R4=inv63
> > R5=inv(id=0,umax_value=4294967295,var_off=(0x0; 0x)) R6_w=inv0
> > R7=invP8 R8=map_pt?
> > 2233: (18) r3 = 0x8881f03f7000
> > ; VERIFY(indirect->map_type == direct->map_type);
> > 2235: (85) call unknown#195896080
> > invalid func unknown#195896080
> > processed 4678 insns (limit 100) max_states_per_insn 9
> > total_states 240 peak_states 178 mark_read 11
> >
> > libbpf: -- END LOG --
> > libbpf: failed to load program 'cgroup_skb/egress'
> > libbpf: failed to load object 'map_ptr_kern'
> > libbpf: failed to load BPF skeleton 'map_ptr_kern': -4007
> > test_map_ptr:FAIL:skel_open_load open_load failed
> > #43 map_ptr:FAIL
> >
> > Above 'invalid func unknown#195896080' happens
> > when libbpf fails to do a relocation at runtime.
> > Please debug.
> > It's certainly caused by this set, but not sure why.
>
> So I've ended up bisecting and debugging it.
> It turned out that the patch 1 was responsible.
> I've added the following hunk to fix it:

Thanks for fixing and debugging it.

> diff --git a/tools/testing/selftests/bpf/progs/map_ptr_kern.c
> b/tools/testing/selftests/bpf/progs/map_ptr_kern.c
> index 473665cac67e..982a2d8aa844 100644
> --- a/tools/testing/selftests/bpf/progs/map_ptr_kern.c
> +++ b/tools/testing/selftests/bpf/progs/map_ptr_kern.c
> @@ -589,7 +589,7 @@ static inline int check_stack(void)
> return 1;

[...]

> and pushed the whole set.
> In the future please always run test_progs and test_progs-no_alu32

Noted, I do run them but this test gave me a different error and I always
ended up ignoring this:

./test_progs -t map_ptr
libbpf: Error in bpf_create_map_xattr(m_array_of_maps):ERROR:
strerror_r(-524)=22(-524). Retrying without BTF.
libbpf: Error in bpf_create_map_xattr(m_hash_of_maps):ERROR:
strerror_r(-524)=22(-524). Retrying without BTF.
libbpf: Error in bpf_create_map_xattr(m_perf_event_array):ERROR:
strerror_r(-524)=22(-524). Retrying without BTF.
libbpf: Error in bpf_create_map_xattr(m_stack_trace):ERROR:
strerror_r(-524)=22(-524). Retrying without BTF.
libbpf: Error in bpf_create_map_xattr(m_cgroup_array):ERROR:
strerror_r(-524)=22(-524). Retrying without BTF.
libbpf: Error in bpf_create_map_xattr(m_devmap):ERROR:
strerror_r(-524)=22(-524). Retrying without BTF.
libbpf: Error in bpf_create_map_xattr(m_sockmap):Invalid
argument(-22). Retrying without BTF.
libbpf: map 'm_sockmap': failed to create: Invalid argument(-22)
libbpf: failed to load object 'map_ptr_kern'
libbpf: failed to load BPF skeleton 'map_ptr_kern': -22
test_map_ptr:FAIL:skel_open_load open_load failed

I now realized that I was not sourcing
tools/testing/selftests/bpf/config correctly
and CONFIG_BPF_STREAM_PARSER was not enabled in my configuration.

Nonetheless, no excuses and will ensure these tests pass in the future.

- KP

> for every patch and submit patches only if _all_ tests are passing.
> Do not assume that your change is not responsible for breakage.


Re: [PATCH bpf-next v8 3/7] bpf: Generalize bpf_sk_storage

2020-08-19 Thread KP Singh



On 8/18/20 3:05 AM, Martin KaFai Lau wrote:
> On Mon, Aug 03, 2020 at 06:46:51PM +0200, KP Singh wrote:
>> From: KP Singh 
>>
>> Refactor the functionality in bpf_sk_storage.c so that concept of
>> storage linked to kernel objects can be extended to other objects like
>> inode, task_struct etc.
>>
>> Each new local storage will still be a separate map and provide its own
>> set of helpers. This allows for future object specific extensions and
>> still share a lot of the underlying implementation.
>>
>> This includes the changes suggested by Martin in:
>>
>>   https://lore.kernel.org/bpf/20200725013047.4006241-1-ka...@fb.com/
>>
>> which adds map_local_storage_charge, map_local_storage_uncharge,
>> and map_owner_storage_ptr.
> A description will still be useful in the commit message to talk
> about the new map_ops, e.g.
> they allow kernel object to optionally have different mem-charge strategy.
> 
>>
>> Co-developed-by: Martin KaFai Lau 
>> Signed-off-by: KP Singh 
>> ---
>>  include/linux/bpf.h|   9 ++
>>  include/net/bpf_sk_storage.h   |  51 +++
>>  include/uapi/linux/bpf.h   |   8 +-
>>  net/core/bpf_sk_storage.c  | 246 +
>>  tools/include/uapi/linux/bpf.h |   8 +-
>>  5 files changed, 233 insertions(+), 89 deletions(-)
>>
>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>> index cef4ef0d2b4e..8e1e23c60dc7 100644
>> --- a/include/linux/bpf.h
>> +++ b/include/linux/bpf.h
>> @@ -34,6 +34,9 @@ struct btf_type;
>>  struct exception_table_entry;
>>  struct seq_operations;
>>  struct bpf_iter_aux_info;
>> +struct bpf_local_storage;
>> +struct bpf_local_storage_map;
>> +struct bpf_local_storage_elem;
> "struct bpf_local_storage_elem" is not needed.

True, I moved it to bpf_sk_storage.h because it's needed there.

> 
>>  
>>  extern struct idr btf_idr;
>>  extern spinlock_t btf_idr_lock;
>> @@ -104,6 +107,12 @@ struct bpf_map_ops {
>>  __poll_t (*map_poll)(struct bpf_map *map, struct file *filp,
>>   struct poll_table_struct *pts);
>>  
>> +/* Functions called by bpf_local_storage maps */
>> +int (*map_local_storage_charge)(struct bpf_local_storage_map *smap,
>> +void *owner, u32 size);
>> +void (*map_local_storage_uncharge)(struct bpf_local_storage_map *smap,
>> +   void *owner, u32 size);
>> +struct bpf_local_storage __rcu ** (*map_owner_storage_ptr)(void *owner);


[...]

>> +struct bpf_local_storage_map *smap,
>> +struct bpf_local_storage_elem *first_selem);
>> +
>> +struct bpf_local_storage_data *
>> +bpf_local_storage_update(void *owner, struct bpf_map *map, void *value,
> Nit.  It may be more consistent to take "struct bpf_local_storage_map *smap"
> instead of "struct bpf_map *map" here.
> 
> bpf_local_storage_map_check_btf() will be the only one taking
> "struct bpf_map *map".

That's because it is used in map operations as map_check_btf which expects
a bpf_map *map pointer. We can wrap it in another function but is that
worth doing? 

> 
>> + u64 map_flags);
>> +
>>  #ifdef CONFIG_BPF_SYSCALL
>>  int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk);
>>  struct bpf_sk_storage_diag *
>> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
>> index b134e679e9db..35629752cec8 100644
>> --- a/include/uapi/linux/bpf.h
>> +++ b/include/uapi/linux/bpf.h
>> @@ -3647,9 +3647,13 @@ enum {
>>  BPF_F_SYSCTL_BASE_NAME  = (1ULL << 0),
>>  };
>>  
>> -/* BPF_FUNC_sk_storage_get flags */
>> +/* BPF_FUNC__storage_get flags */
> BPF_FUNC__storage_get flags?
> 

Done.

>>  enum {
>> -BPF_SK_STORAGE_GET_F_CREATE = (1ULL << 0),
>> +BPF_LOCAL_STORAGE_GET_F_CREATE  = (1ULL << 0),
>> +/* BPF_SK_STORAGE_GET_F_CREATE is only kept for backward compatibility
>> + * and BPF_LOCAL_STORAGE_GET_F_CREATE must be used instead.
>> + */
>> +BPF_SK_STORAGE_GET_F_CREATE  = BPF_LOCAL_STORAGE_GET_F_CREATE,
>>  };
>>  
>>  /* BPF_FUNC_read_branch_records flags. */
>> diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c
>> index 99dde7b74767..bb2375769ca1 100644
>> --- a/net/core/bpf_sk_storage.c
>> +++ b/net/core/bpf_sk_storage.c
>> @@ -84,7 +84,7 @@ struct bpf_local_storage_elem {
>>  struct bpf_local_s

Re: [PATCH bpf-next v8 6/7] bpf: Allow local storage to be used from LSM programs

2020-08-19 Thread KP Singh



On 8/18/20 6:16 AM, Martin KaFai Lau wrote:
> On Mon, Aug 03, 2020 at 06:46:54PM +0200, KP Singh wrote:
>> From: KP Singh 
>>
>> Adds support for both bpf_{sk, inode}_storage_{get, delete} to be used
>> in LSM programs. These helpers are not used for tracing programs

[...]

>> @@ -2823,6 +2823,10 @@ union bpf_attr {
>>   *  "type". The bpf-local-storage "type" (i.e. the *map*) is
>>   *  searched against all bpf-local-storages residing at *sk*.
>>   *
>> + *  For socket programs, *sk* should be a **struct bpf_sock** 
>> pointer
>> + *  and an **ARG_PTR_TO_BTF_ID** of type **struct sock** for LSM
>> + *  programs.
> I found it a little vague on what "socket programs" is.  May be:
> 
> *sk* is a kernel **struct sock** pointer for LSM program.
> *sk* is a **struct bpf_sock** pointer for other program types.

This is better, Thanks!

- KP

> 
> Others LGTM
> 
> Acked-by: Martin KaFai Lau 
> 


Re: [PATCH bpf-next v8 3/7] bpf: Generalize bpf_sk_storage

2020-08-19 Thread KP Singh



On 19.08.20 19:12, Martin KaFai Lau wrote:
> On Wed, Aug 19, 2020 at 02:41:50PM +0200, KP Singh wrote:
>> On 8/18/20 3:05 AM, Martin KaFai Lau wrote:
>>> On Mon, Aug 03, 2020 at 06:46:51PM +0200, KP Singh wrote:
>>>> From: KP Singh 
>>>>
>>>> Refactor the functionality in bpf_sk_storage.c so that concept of

[...]

>>>> +  struct bpf_local_storage_map *smap,
>>>> +  struct bpf_local_storage_elem *first_selem);
>>>> +
>>>> +struct bpf_local_storage_data *
>>>> +bpf_local_storage_update(void *owner, struct bpf_map *map, void *value,
>>> Nit.  It may be more consistent to take "struct bpf_local_storage_map *smap"
>>> instead of "struct bpf_map *map" here.
>>>
>>> bpf_local_storage_map_check_btf() will be the only one taking
>>> "struct bpf_map *map".
>>
>> That's because it is used in map operations as map_check_btf which expects
>> a bpf_map *map pointer. We can wrap it in another function but is that
>> worth doing?
> Agree.  bpf_local_storage_map_check_btf() should stay as is.
> 
> I meant to only change the "bpf_local_storage_update()" to take
> "struct bpf_local_storage_map *smap".
> 

Apologies, I misread that. Updated.

- KP

 up here
>>   * or when the storage is freed e.g.
>>   * by bpf_sk_storage_free() during __sk_destruct().
>>
> +1
> 


Re: [RFC] security: replace indirect calls with static calls

2020-08-20 Thread KP Singh
On Thu, Aug 20, 2020 at 8:43 PM James Morris  wrote:
>
> On Thu, 20 Aug 2020, Brendan Jackman wrote:
>
> > With this implementation, any overhead of the indirect call in the LSM
> > framework is completely mitigated (performance results: [7]). This
> > facilitates the adoption of "bpf" LSM on production machines and also
> > benefits all other LSMs.
>
> This looks like a potentially useful improvement, although I wonder if it
> would be overshadowed by an LSM hook doing real work.
>

Thanks for taking a look!

We can surely look at other examples, but the real goal is to
optimize the case where the "bpf" LSM adds callbacks to every LSM hook
which don't do any real work and cause an avoidable overhead.

This makes it not very practical for data center environments where
one would want a framework that adds a zero base case overhead and
allows the user to decide where to hook / add performance penalties.
(at boot time for other LSMs and at runtime for bpf)

I also think this would be beneficial for LSMs which use a cache for
a faster policy decision (e.g. access vector caching in SELinux).

- KP

> Do you have any more benchmarking beyond eventfd_write() ?
>
>
>
> >
> > [1]: https://lwn.net/ml/linux-kernel/20200710133831.943894...@infradead.org/

[...]

> >
> >  /* Security operations */
> >
>
> --
> James Morris
> 
>


Re: [PATCH bpf-next v8 1/7] A purely mechanical change to split the renaming from the actual generalization.

2020-08-18 Thread KP Singh



On 8/18/20 1:56 AM, Martin KaFai Lau wrote:
> On Mon, Aug 03, 2020 at 06:46:49PM +0200, KP Singh wrote:
>> From: KP Singh 
>>
>> Flags/consts:
>>
>>   SK_STORAGE_CREATE_FLAG_MASKBPF_LOCAL_STORAGE_CREATE_FLAG_MASK
>>   BPF_SK_STORAGE_CACHE_SIZE  BPF_LOCAL_STORAGE_CACHE_SIZE
>>   MAX_VALUE_SIZE BPF_LOCAL_STORAGE_MAX_VALUE_SIZE
>>
>> Structs:
>>
>>   bucket bpf_local_storage_map_bucket
>>   bpf_sk_storage_map bpf_local_storage_map
>>   bpf_sk_storage_databpf_local_storage_data
>>   bpf_sk_storage_elembpf_local_storage_elem
>>   bpf_sk_storage bpf_local_storage
>>
>> The "sk" member in bpf_local_storage is also updated to "owner"
>> in preparation for changing the type to void * in a subsequent patch.
>>
>> Functions:
>>
>>   selem_linked_to_sk selem_linked_to_storage
>>   selem_allocbpf_selem_alloc
>>   __selem_unlink_sk  bpf_selem_unlink_storage
>>   __selem_link_skbpf_selem_link_storage
>>   selem_unlink_sk__bpf_selem_unlink_storage
>>   sk_storage_update  bpf_local_storage_update
>>   __sk_storage_lookupbpf_local_storage_lookup
>>   bpf_sk_storage_map_freebpf_local_storage_map_free
>>   bpf_sk_storage_map_alloc   bpf_local_storage_map_alloc
>>   bpf_sk_storage_map_alloc_check bpf_local_storage_map_alloc_check
>>   bpf_sk_storage_map_check_btf   bpf_local_storage_map_check_btf
>>
> 
> [ ... ]
> 
>> @@ -147,85 +148,86 @@ static struct bpf_sk_storage_elem *selem_alloc(struct 
>> bpf_sk_storage_map *smap,
>>   * The caller must ensure selem->smap is still valid to be
>>   * dereferenced for its smap->elem_size and smap->cache_idx.
>>   */
>> -static bool __selem_unlink_sk(struct bpf_sk_storage *sk_storage,
>> -  struct bpf_sk_storage_elem *selem,
>> -  bool uncharge_omem)
>> +static bool bpf_selem_unlink_storage(struct bpf_local_storage 
>> *local_storage,
>> + struct bpf_local_storage_elem *selem,
>> + bool uncharge_omem)
> Please add a "_nolock()" suffix, just to be clear that the unlink_map()
> counter part is locked.  It could be a follow up later.

Done.

> 
>>  {
>> -struct bpf_sk_storage_map *smap;
>> -bool free_sk_storage;
>> +struct bpf_local_storage_map *smap;
>> +bool free_local_storage;

[...]

>> +if (unlikely(!selem_linked_to_storage(selem)))
>>  /* selem has already been unlinked from sk */
>>  return;
>>  
>> -sk_storage = rcu_dereference(selem->sk_storage);
>> -raw_spin_lock_bh(_storage->lock);
>> -if (likely(selem_linked_to_sk(selem)))
>> -free_sk_storage = __selem_unlink_sk(sk_storage, selem, true);
>> -raw_spin_unlock_bh(_storage->lock);
>> +local_storage = rcu_dereference(selem->local_storage);
>> +raw_spin_lock_bh(_storage->lock);
>> +if (likely(selem_linked_to_storage(selem)))
>> +free_local_storage =
>> +bpf_selem_unlink_storage(local_storage, selem, true);
>> +raw_spin_unlock_bh(_storage->lock);
>>  
>> -if (free_sk_storage)
>> -kfree_rcu(sk_storage, rcu);
>> +if (free_local_storage)
>> +kfree_rcu(local_storage, rcu);
>>  }
>>  
>> -static void __selem_link_sk(struct bpf_sk_storage *sk_storage,
>> -struct bpf_sk_storage_elem *selem)
>> +static void bpf_selem_link_storage(struct bpf_local_storage *local_storage,
>> +   struct bpf_local_storage_elem *selem)
> Same here. bpf_selem_link_storage"_nolock"().

Done.

> 
> Please tag the Subject line with "bpf:".

Done. Changed it to:

bpf: Renames in preparation for bpf_local_storage
    
A purely mechanical change to split the renaming from the actual
generalization.

[...]

> 
> Acked-by: Martin KaFai Lau 
> 


Re: [PATCH bpf-next v8 5/7] bpf: Implement bpf_local_storage for inodes

2020-08-18 Thread KP Singh



On 8/18/20 3:27 AM, Martin KaFai Lau wrote:
> On Mon, Aug 03, 2020 at 06:46:53PM +0200, KP Singh wrote:
>> From: KP Singh 
>>
>> Similar to bpf_local_storage for sockets, add local storage for inodes.
>> The life-cycle of storage is managed with the life-cycle of the inode.
>> i.e. the storage is destroyed along with the owning inode.
>>
>> The BPF LSM allocates an __rcu pointer to the bpf_local_storage in the
>> security blob which are now stackable and can co-exist with other LSMs.
>>
>> Signed-off-by: KP Singh 
>> ---
>>  include/linux/bpf_local_storage.h |  10 +
>>  include/linux/bpf_lsm.h   |  21 ++
>>  include/linux/bpf_types.h |   3 +
>>  include/uapi/linux/bpf.h  |  38 +++
>>  kernel/bpf/Makefile   |   1 +

[...]

ata *inode_storage_lookup(struct inode *inode,
>> +   struct bpf_map *map,
>> +   bool cacheit_lockit)
>> +{
>> +struct bpf_local_storage *inode_storage;
>> +struct bpf_local_storage_map *smap;
>> +struct bpf_storage_blob *bsb;
>> +
>> +bsb = bpf_inode(inode);
>> +if (!bsb)
>> +return ERR_PTR(-ENOENT);
> ERR_PTR is returned here...
> 
>> +
>> +inode_storage = rcu_dereference(bsb->storage);
>> +if (!inode_storage)
>> +return NULL;
>> +

[...]

>> +kfree_rcu(local_storage, rcu);
>> +}
>> +
>> +
>> +static void *bpf_fd_inode_storage_lookup_elem(struct bpf_map *map, void 
>> *key)
>> +{
>> +struct bpf_local_storage_data *sdata;
>> +struct file *f;
>> +int fd;
>> +
>> +fd = *(int *)key;
>> +f = fcheck(fd);
>> +if (!f)
>> +return ERR_PTR(-EINVAL);
>> +
>> +get_file(f);
>> +sdata = inode_storage_lookup(f->f_inode, map, true);
>> +fput(f);
>> +return sdata ? sdata->data : NULL;
> sdata can be ERR_PTR here and a few other cases below.
> 
> May be inode_storage_lookup() should just return NULL.

I think returning NULL is a better option. Thanks!

> 
>> +}
>> +
>> +static int bpf_fd_inode_storage_update_elem(struct bpf_map *map, void *key,
>> + void *value, u64 map_flags)
>> +{
>> +struct bpf_local_storage_data *sdata;
>> +struct file *f;
>> +int fd;
>> +
>> +fd = *(int *)key;
>> +f = fcheck(fd);
>> +if (!f)
>> +return -EINVAL;
>> +
>> +get_file(f);> get_file() does atomic_long_inc() instead of 
>> atomic_long_inc_not_zero().
> I don't see how that helps here.  Am I missing something?

You are right, this should not not be an fcheck followed by a get_file
rather fcheck followed by get_file_rcu:

#define get_file_rcu_many(x, cnt)   \
atomic_long_add_unless(&(x)->f_count, (cnt), 0)
#define get_file_rcu(x) get_file_rcu_many((x), 1)
#define file_count(x)   atomic_long_read(&(x)->f_count)

But there is an easier way than all of this and this is to use 
fget_raw which also calls get_file_rcu_many 
and ensures a non-zero count before getting a reference.

- KP

> 
>> +sdata = bpf_local_storage_update(f->f_inode, map, value, map_flags);
>> +fput(f);
>> +return PTR_ERR_OR_ZERO(sdata);
>> +}
>> +


Re: [PATCH bpf-next v8 5/7] bpf: Implement bpf_local_storage for inodes

2020-08-18 Thread KP Singh



On 8/18/20 5:23 PM, Martin KaFai Lau wrote:
> On Tue, Aug 18, 2020 at 05:10:34PM +0200, KP Singh wrote:
>>
>>
>> On 8/18/20 3:27 AM, Martin KaFai Lau wrote:>>> On Mon, Aug 03, 2020 at 
>> 06:46:53PM +0200, KP Singh wrote:

[...]

a get_file
>> rather fcheck followed by get_file_rcu:
>>
>> #define get_file_rcu_many(x, cnt)\
>>  atomic_long_add_unless(&(x)->f_count, (cnt), 0)
>> #define get_file_rcu(x) get_file_rcu_many((x), 1)
>> #define file_count(x)atomic_long_read(&(x)->f_count)
>>
>> But there is an easier way than all of this and this is to use 
>> fget_raw which also calls get_file_rcu_many 
>> and ensures a non-zero count before getting a reference.
> ic. Make sense.
> 
> There are fdget() and fdput() also which are used in bpf/syscall.c.

Yeah we could use fdget_raw but we don't really need the struct fd but just the 
struct file.

he non-raw versions masks away FMODE_PATH (O_PATH) files, we should still be 
able to
access blobs on the O_PATH files, thus the _raw version here.

- KP

> 


Re: [PATCH v4 09/17] LSM: Introduce kernel_post_load_data() hook

2020-08-06 Thread KP Singh
On Wed, Jul 29, 2020 at 7:59 PM Kees Cook  wrote:
>
> There are a few places in the kernel where LSMs would like to have
> visibility into the contents of a kernel buffer that has been loaded or
> read. While security_kernel_post_read_file() (which includes the
> buffer) exists as a pairing for security_kernel_read_file(), no such
> hook exists to pair with security_kernel_load_data().
>
> Earlier proposals for just using security_kernel_post_read_file() with a
> NULL file argument were rejected (i.e. "file" should always be valid for
> the security_..._file hooks, but it appears at least one case was
> left in the kernel during earlier refactoring. (This will be fixed in
> a subsequent patch.)
>
> Since not all cases of security_kernel_load_data() can have a single
> contiguous buffer made available to the LSM hook (e.g. kexec image
> segments are separately loaded), there needs to be a way for the LSM to
> reason about its expectations of the hook coverage. In order to handle
> this, add a "contents" argument to the "kernel_load_data" hook that
> indicates if the newly added "kernel_post_load_data" hook will be called
> with the full contents once loaded. That way, LSMs requiring full contents
> can choose to unilaterally reject "kernel_load_data" with contents=false
> (which is effectively the existing hook coverage), but when contents=true
> they can allow it and later evaluate the "kernel_post_load_data" hook
> once the buffer is loaded.
>
> With this change, LSMs can gain coverage over non-file-backed data loads
> (e.g. init_module(2) and firmware userspace helper), which will happen
> in subsequent patches.
>
> Additionally prepare IMA to start processing these cases.
>
> Signed-off-by: Kees Cook 

Thanks for adding this! Would be really useful for us.

Reviewed-by: KP Singh 

> ---
>  drivers/base/firmware_loader/fallback.c   |  2 +-

[...]

> index 5de45010fb1a..1a5c68196faf 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -4019,7 +4019,7 @@ static int selinux_kernel_read_file(struct file *file,
> return rc;
>  }
>
> -static int selinux_kernel_load_data(enum kernel_load_data_id id)
> +static int selinux_kernel_load_data(enum kernel_load_data_id id, bool 
> contents)
>  {
> int rc = 0;
>
> --
> 2.25.1
>


Re: [PATCH v4 11/17] module: Call security_kernel_post_load_data()

2020-08-06 Thread KP Singh
On Wed, Aug 5, 2020 at 4:53 PM Jessica Yu  wrote:
>
> +++ Kees Cook [29/07/20 10:58 -0700]:
> >Now that there is an API for checking loaded contents for modules
> >loaded without a file, call into the LSM hooks.
> >
> >Cc: Jessica Yu 
> >Signed-off-by: Kees Cook 
>
> Acked-by: Jessica Yu 

Thanks!

Reviewed-by: KP Singh 


Re: [PATCH bpf-next v2 7/8] bpf: Add tests for task_local_storage

2020-11-04 Thread KP Singh
[...]

> > Ahh. Yes. That should do it. Right now I don't see concerns with safety
> > of the bpf_spin_lock in bpf_lsm progs.
>
> What about sleepable lsm hooks? Normally we wouldn't expect to sleep with
> a spinlock held. Should we have a check to ensure programs bpf_spin_lock
> are not also sleepable?

Thanks. Yes, I added that to my patch:

diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 61f8cc52fd5b..93383df2140b 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -63,6 +63,10 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const
struct bpf_prog *prog)
return _task_storage_get_proto;
case BPF_FUNC_task_storage_delete:
return _task_storage_delete_proto;
+   case BPF_FUNC_spin_lock:
+   return _spin_lock_proto;
+   case BPF_FUNC_spin_unlock:
+   return _spin_unlock_proto;
default:
return tracing_prog_func_proto(func_id, prog);
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 314018e8fc12..8892f7ba2041 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9739,6 +9739,23 @@ static int check_map_prog_compatibility(struct
bpf_verifier_env *env,
return -EINVAL;
}

+   if (map_value_has_spin_lock(map)) {
+   if (prog_type == BPF_PROG_TYPE_SOCKET_FILTER) {
+   verbose(env, "socket filter progs cannot use
bpf_spin_lock yet\n");
+   return -EINVAL;
+   }
+
+   if (is_tracing_prog_type(prog_type)) {
+   verbose(env, "tracing progs cannot use
bpf_spin_lock yet\n");
+   return -EINVAL;
+   }
+
+   if (prog->aux->sleepable) {
+   verbose(env, "sleepable progs cannot use
bpf_spin_lock\n");
+   return -EINVAL;
+   }
+   }
+


Re: [PATCH bpf-next v2 7/8] bpf: Add tests for task_local_storage

2020-11-04 Thread KP Singh
On Wed, Nov 4, 2020 at 12:03 PM KP Singh  wrote:
>
> [...]
>
> > > Ahh. Yes. That should do it. Right now I don't see concerns with safety
> > > of the bpf_spin_lock in bpf_lsm progs.
> >
> > What about sleepable lsm hooks? Normally we wouldn't expect to sleep with
> > a spinlock held. Should we have a check to ensure programs bpf_spin_lock
> > are not also sleepable?
>
> Thanks. Yes, I added that to my patch:
>
> diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> index 61f8cc52fd5b..93383df2140b 100644
> --- a/kernel/bpf/bpf_lsm.c
> +++ b/kernel/bpf/bpf_lsm.c
> @@ -63,6 +63,10 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const
> struct bpf_prog *prog)
> return _task_storage_get_proto;
> case BPF_FUNC_task_storage_delete:
> return _task_storage_delete_proto;
> +   case BPF_FUNC_spin_lock:
> +   return _spin_lock_proto;
> +   case BPF_FUNC_spin_unlock:
> +   return _spin_unlock_proto;
> default:
> return tracing_prog_func_proto(func_id, prog);
> }
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 314018e8fc12..8892f7ba2041 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -9739,6 +9739,23 @@ static int check_map_prog_compatibility(struct
> bpf_verifier_env *env,
> return -EINVAL;
> }
>
> +   if (map_value_has_spin_lock(map)) {
> +   if (prog_type == BPF_PROG_TYPE_SOCKET_FILTER) {
> +   verbose(env, "socket filter progs cannot use
> bpf_spin_lock yet\n");
> +   return -EINVAL;
> +   }
> +
> +   if (is_tracing_prog_type(prog_type)) {
> +   verbose(env, "tracing progs cannot use
> bpf_spin_lock yet\n");
> +   return -EINVAL;
> +   }
> +
> +   if (prog->aux->sleepable) {
> +   verbose(env, "sleepable progs cannot use
> bpf_spin_lock\n");

I think this can still be "yet" as it's doable; we can disable/enable
preemption in the helpers
and then have the verifier track that no sleepable helper is called
when a spin lock is held.
I would, however, prefer if we do it in a subsequent patch.

- KP

> +   return -EINVAL;
> +   }
> +   }
> +


[PATCH bpf-next v3 0/9] Implement task_local_storage

2020-11-04 Thread KP Singh
From: KP Singh 

# v2 -> v3

- Added bpf_spin_locks to the selftests for local storage, found that
  these are not available for LSM programs.
- Made spin lock helpers available for LSM programs (except sleepable
  programs which need more work).
- Minor fixes for includes and added short commit messages for patches
  that were split up for libbpf and bpftool.
- Added Song's acks.

# v1 -> v2

- Updated the refcounting for task_struct and simplified conversion
  of fd -> struct pid.
- Some fixes suggested by Martin and Andrii, notably:
   * long return type for the bpf_task_storage_delete helper (update
 for bpf_inode_storage_delete will be sent separately).
   * Remove extra nullness check to task_storage_ptr in map syscall
 ops.
   * Changed the argument signature of the BPF helpers to use
 task_struct pointer in uapi headers.
   * Remove unnecessary verifier logic for the bpf_get_current_task_btf
 helper.
   * Split the changes for bpftool and libbpf.
- Exercised syscall operations for local storage (kept a simpler verison
  in test_local_storage.c, the eventual goal will be to update
  sk_storage_map.c for all local storage types).
- Formatting fixes + Rebase.

We already have socket and inode local storage since [1]

This patch series:

* Implements bpf_local_storage for task_struct.
* Implements the bpf_get_current_task_btf helper which returns a BTF
  pointer to the current task. Not only is this generally cleaner
  (reading from the task_struct currently requires BPF_CORE_READ), it
  also allows the BTF pointer to be used in task_local_storage helpers.
* In order to implement this helper, a RET_PTR_TO_BTF_ID is introduced
  which works similar to RET_PTR_TO_BTF_ID_OR_NULL but does not require
  a nullness check.
* Implements a detection in selftests which uses the
  task local storage to deny a running executable from unlinking itself.

[1]: 
https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=f836a56e84ffc9f1a1cd73f77e10404ca46a4616

KP Singh (9):
  bpf: Implement task local storage
  libbpf: Add support for task local storage
  bpftool: Add support for task local storage
  bpf: Implement get_current_task_btf and RET_PTR_TO_BTF_ID
  bpf: Allow LSM programs to use bpf spin locks
  bpf: Fix tests for local_storage
  bpf: Update selftests for local_storage to use vmlinux.h
  bpf: Add tests for task_local_storage
  bpf: Exercise syscall operations for inode and sk storage

 include/linux/bpf.h   |   1 +
 include/linux/bpf_lsm.h   |  23 ++
 include/linux/bpf_types.h |   1 +
 include/uapi/linux/bpf.h  |  48 +++
 kernel/bpf/Makefile   |   1 +
 kernel/bpf/bpf_lsm.c  |   8 +
 kernel/bpf/bpf_task_storage.c | 313 ++
 kernel/bpf/syscall.c  |   3 +-
 kernel/bpf/verifier.c |  34 +-
 kernel/trace/bpf_trace.c  |  16 +
 security/bpf/hooks.c  |   2 +
 .../bpf/bpftool/Documentation/bpftool-map.rst |   3 +-
 tools/bpf/bpftool/bash-completion/bpftool |   2 +-
 tools/bpf/bpftool/map.c   |   4 +-
 tools/include/uapi/linux/bpf.h|  48 +++
 tools/lib/bpf/libbpf_probes.c |   1 +
 .../bpf/prog_tests/test_local_storage.c   | 182 +-
 .../selftests/bpf/progs/local_storage.c   | 103 --
 18 files changed, 741 insertions(+), 52 deletions(-)
 create mode 100644 kernel/bpf/bpf_task_storage.c

-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v3 3/9] bpftool: Add support for task local storage

2020-11-04 Thread KP Singh
From: KP Singh 

Updates the binary to handle the BPF_MAP_TYPE_TASK_STORAGE as
"task_storage" for printing and parsing. Also updates the documentation
and bash completion

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 tools/bpf/bpftool/Documentation/bpftool-map.rst | 3 ++-
 tools/bpf/bpftool/bash-completion/bpftool   | 2 +-
 tools/bpf/bpftool/map.c | 4 +++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst 
b/tools/bpf/bpftool/Documentation/bpftool-map.rst
index dade10cdf295..3d52256ba75f 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-map.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst
@@ -50,7 +50,8 @@ MAP COMMANDS
 |  | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | 
**hash_of_maps**
 |  | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | 
**xskmap** | **sockhash**
 |  | **cgroup_storage** | **reuseport_sockarray** | 
**percpu_cgroup_storage**
-|  | **queue** | **stack** | **sk_storage** | **struct_ops** | 
**ringbuf** | **inode_storage** }
+|  | **queue** | **stack** | **sk_storage** | **struct_ops** | 
**ringbuf** | **inode_storage**
+   | **task_storage** }
 
 DESCRIPTION
 ===
diff --git a/tools/bpf/bpftool/bash-completion/bpftool 
b/tools/bpf/bpftool/bash-completion/bpftool
index 3f1da30c4da6..fdffbc64c65c 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -705,7 +705,7 @@ _bpftool()
 hash_of_maps devmap devmap_hash sockmap cpumap 
\
 xskmap sockhash cgroup_storage 
reuseport_sockarray \
 percpu_cgroup_storage queue stack sk_storage \
-struct_ops inode_storage' -- \
+struct_ops inode_storage task_storage' -- \
"$cur" ) )
 return 0
 ;;
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index a7efbd84fbcc..b400364ee054 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -51,6 +51,7 @@ const char * const map_type_name[] = {
[BPF_MAP_TYPE_STRUCT_OPS]   = "struct_ops",
[BPF_MAP_TYPE_RINGBUF]  = "ringbuf",
[BPF_MAP_TYPE_INODE_STORAGE]= "inode_storage",
+   [BPF_MAP_TYPE_TASK_STORAGE] = "task_storage",
 };
 
 const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
@@ -1464,7 +1465,8 @@ static int do_help(int argc, char **argv)
" lru_percpu_hash | lpm_trie | array_of_maps | 
hash_of_maps |\n"
" devmap | devmap_hash | sockmap | cpumap | 
xskmap | sockhash |\n"
" cgroup_storage | reuseport_sockarray | 
percpu_cgroup_storage |\n"
-   " queue | stack | sk_storage | struct_ops | 
ringbuf | inode_storage }\n"
+   " queue | stack | sk_storage | struct_ops | 
ringbuf | inode_storage |\n"
+   " task_storage }\n"
"   " HELP_SPEC_OPTIONS "\n"
"",
bin_name, argv[-2]);
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v3 9/9] bpf: Exercise syscall operations for inode and sk storage

2020-11-04 Thread KP Singh
From: KP Singh 

Use the check_syscall_operations added for task_local_storage to
exercise syscall operations for other local storage maps:

* Check the absence of an element for the given fd.
* Create a new element, retrieve and compare its value.
* Delete the element and check again for absence.

Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c | 17 +++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
index feba23f8848b..48c9237f1314 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -145,7 +145,7 @@ bool check_syscall_operations(int map_fd, int obj_fd)
 void test_test_local_storage(void)
 {
char tmp_exec_path[PATH_MAX] = "/tmp/copy_of_rmXX";
-   int err, serv_sk = -1, task_fd = -1;
+   int err, serv_sk = -1, task_fd = -1, rm_fd = -1;
struct local_storage *skel = NULL;
 
skel = local_storage__open_and_load();
@@ -169,6 +169,15 @@ void test_test_local_storage(void)
if (CHECK(err < 0, "copy_rm", "err %d errno %d\n", err, errno))
goto close_prog;
 
+   rm_fd = open(tmp_exec_path, O_RDONLY);
+   if (CHECK(rm_fd < 0, "open", "failed to open %s err:%d, errno:%d",
+ tmp_exec_path, rm_fd, errno))
+   goto close_prog;
+
+   if (!check_syscall_operations(bpf_map__fd(skel->maps.inode_storage_map),
+ rm_fd))
+   goto close_prog;
+
/* Sets skel->bss->monitored_pid to the pid of the forked child
 * forks a child process that executes tmp_exec_path and tries to
 * unlink its executable. This operation should be denied by the loaded
@@ -197,9 +206,13 @@ void test_test_local_storage(void)
CHECK(skel->data->sk_storage_result != 0, "sk_storage_result",
  "sk_local_storage not set\n");
 
-   close(serv_sk);
+   if (!check_syscall_operations(bpf_map__fd(skel->maps.sk_storage_map),
+ serv_sk))
+   goto close_prog;
 
 close_prog:
+   close(serv_sk);
+   close(rm_fd);
close(task_fd);
local_storage__destroy(skel);
 }
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v3 4/9] bpf: Implement get_current_task_btf and RET_PTR_TO_BTF_ID

2020-11-04 Thread KP Singh
From: KP Singh 

The currently available bpf_get_current_task returns an unsigned integer
which can be used along with BPF_CORE_READ to read data from
the task_struct but still cannot be used as an input argument to a
helper that accepts an ARG_PTR_TO_BTF_ID of type task_struct.

In order to implement this helper a new return type, RET_PTR_TO_BTF_ID,
is added. This is similar to RET_PTR_TO_BTF_ID_OR_NULL but does not
require checking the nullness of returned pointer.

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 include/linux/bpf.h|  1 +
 include/uapi/linux/bpf.h   |  9 +
 kernel/bpf/verifier.c  |  7 +--
 kernel/trace/bpf_trace.c   | 16 
 tools/include/uapi/linux/bpf.h |  9 +
 5 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2fffd30e13ac..73d5381a5d5c 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -310,6 +310,7 @@ enum bpf_return_type {
RET_PTR_TO_BTF_ID_OR_NULL,  /* returns a pointer to a btf_id or 
NULL */
RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL, /* returns a pointer to a valid 
memory or a btf_id or NULL */
RET_PTR_TO_MEM_OR_BTF_ID,   /* returns a pointer to a valid memory 
or a btf_id */
+   RET_PTR_TO_BTF_ID,  /* returns a pointer to a btf_id */
 };
 
 /* eBPF function prototype used by verifier to allow BPF_CALLs from eBPF 
programs
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index f4037b2161a6..9879d6793e90 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -3779,6 +3779,14 @@ union bpf_attr {
  * 0 on success.
  *
  * **-ENOENT** if the bpf_local_storage cannot be found.
+ *
+ * struct task_struct *bpf_get_current_task_btf(void)
+ * Description
+ * Return a BTF pointer to the "current" task.
+ * This pointer can also be used in helpers that accept an
+ * *ARG_PTR_TO_BTF_ID* of type *task_struct*.
+ * Return
+ * Pointer to the current task.
  */
 #define __BPF_FUNC_MAPPER(FN)  \
FN(unspec), \
@@ -3939,6 +3947,7 @@ union bpf_attr {
FN(redirect_peer),  \
FN(task_storage_get),   \
FN(task_storage_delete),\
+   FN(get_current_task_btf),   \
/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index b0790876694f..314018e8fc12 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5186,11 +5186,14 @@ static int check_helper_call(struct bpf_verifier_env 
*env, int func_id, int insn
PTR_TO_BTF_ID : PTR_TO_BTF_ID_OR_NULL;
regs[BPF_REG_0].btf_id = meta.ret_btf_id;
}
-   } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL) {
+   } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL ||
+  fn->ret_type == RET_PTR_TO_BTF_ID) {
int ret_btf_id;
 
mark_reg_known_zero(env, regs, BPF_REG_0);
-   regs[BPF_REG_0].type = PTR_TO_BTF_ID_OR_NULL;
+   regs[BPF_REG_0].type = fn->ret_type == RET_PTR_TO_BTF_ID ?
+PTR_TO_BTF_ID :
+PTR_TO_BTF_ID_OR_NULL;
ret_btf_id = *fn->ret_btf_id;
if (ret_btf_id == 0) {
verbose(env, "invalid return type %d of func %s#%d\n",
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 4517c8b66518..e4515b0f62a8 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1022,6 +1022,20 @@ const struct bpf_func_proto bpf_get_current_task_proto = 
{
.ret_type   = RET_INTEGER,
 };
 
+BPF_CALL_0(bpf_get_current_task_btf)
+{
+   return (unsigned long) current;
+}
+
+BTF_ID_LIST_SINGLE(bpf_get_current_btf_ids, struct, task_struct)
+
+static const struct bpf_func_proto bpf_get_current_task_btf_proto = {
+   .func   = bpf_get_current_task_btf,
+   .gpl_only   = true,
+   .ret_type   = RET_PTR_TO_BTF_ID,
+   .ret_btf_id = _get_current_btf_ids[0],
+};
+
 BPF_CALL_2(bpf_current_task_under_cgroup, struct bpf_map *, map, u32, idx)
 {
struct bpf_array *array = container_of(map, struct bpf_array, map);
@@ -1265,6 +1279,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const 
struct bpf_prog *prog)
return _get_current_pid_tgid_proto;
case BPF_FUNC_get_current_task:
return _get_current_task_proto;
+   case BPF_FUNC_get_current_task_btf:
+   return _get_current_task_btf_proto;
case BPF_FUNC_get_current_uid_gid:
return _get_current_uid_gid_proto;
case BPF_FUNC_get_current_comm:
diff --git

[PATCH bpf-next v3 6/9] bpf: Fix tests for local_storage

2020-11-04 Thread KP Singh
From: KP Singh 

The {inode,sk}_storage_result checking if the correct value was retrieved
was being clobbered unconditionally by the return value of the
bpf_{inode,sk}_storage_delete call.

Also, consistently use the newly added BPF_LOCAL_STORAGE_GET_F_CREATE
flag.

Acked-by: Song Liu 
Fixes: cd324d7abb3d ("bpf: Add selftests for local_storage")
Signed-off-by: KP Singh 
---
 .../selftests/bpf/progs/local_storage.c   | 24 ---
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
index 0758ba229ae0..09529e33be98 100644
--- a/tools/testing/selftests/bpf/progs/local_storage.c
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -58,20 +58,22 @@ int BPF_PROG(unlink_hook, struct inode *dir, struct dentry 
*victim)
 {
__u32 pid = bpf_get_current_pid_tgid() >> 32;
struct dummy_storage *storage;
+   int err;
 
if (pid != monitored_pid)
return 0;
 
storage = bpf_inode_storage_get(_storage_map, victim->d_inode, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+   BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-   if (storage->value == DUMMY_STORAGE_VALUE)
+   if (storage->value != DUMMY_STORAGE_VALUE)
inode_storage_result = -1;
 
-   inode_storage_result =
-   bpf_inode_storage_delete(_storage_map, victim->d_inode);
+   err = bpf_inode_storage_delete(_storage_map, victim->d_inode);
+   if (!err)
+   inode_storage_result = err;
 
return 0;
 }
@@ -82,19 +84,23 @@ int BPF_PROG(socket_bind, struct socket *sock, struct 
sockaddr *address,
 {
__u32 pid = bpf_get_current_pid_tgid() >> 32;
struct dummy_storage *storage;
+   int err;
 
if (pid != monitored_pid)
return 0;
 
storage = bpf_sk_storage_get(_storage_map, sock->sk, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-   if (storage->value == DUMMY_STORAGE_VALUE)
+   if (storage->value != DUMMY_STORAGE_VALUE)
sk_storage_result = -1;
 
-   sk_storage_result = bpf_sk_storage_delete(_storage_map, sock->sk);
+   err = bpf_sk_storage_delete(_storage_map, sock->sk);
+   if (!err)
+   sk_storage_result = err;
+
return 0;
 }
 
@@ -109,7 +115,7 @@ int BPF_PROG(socket_post_create, struct socket *sock, int 
family, int type,
return 0;
 
storage = bpf_sk_storage_get(_storage_map, sock->sk, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
@@ -131,7 +137,7 @@ int BPF_PROG(file_open, struct file *file)
return 0;
 
storage = bpf_inode_storage_get(_storage_map, file->f_inode, 0,
-BPF_LOCAL_STORAGE_GET_F_CREATE);
+   BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v3 7/9] bpf: Update selftests for local_storage to use vmlinux.h

2020-11-04 Thread KP Singh
From: KP Singh 

With the fixing of BTF pruning of embedded types being fixed, the test
can be simplified to use vmlinux.h

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 .../selftests/bpf/progs/local_storage.c   | 20 +--
 1 file changed, 1 insertion(+), 19 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
index 09529e33be98..ef3822bc7542 100644
--- a/tools/testing/selftests/bpf/progs/local_storage.c
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -4,9 +4,8 @@
  * Copyright 2020 Google LLC.
  */
 
+#include "vmlinux.h"
 #include 
-#include 
-#include 
 #include 
 #include 
 
@@ -36,23 +35,6 @@ struct {
__type(value, struct dummy_storage);
 } sk_storage_map SEC(".maps");
 
-/* TODO Use vmlinux.h once BTF pruning for embedded types is fixed.
- */
-struct sock {} __attribute__((preserve_access_index));
-struct sockaddr {} __attribute__((preserve_access_index));
-struct socket {
-   struct sock *sk;
-} __attribute__((preserve_access_index));
-
-struct inode {} __attribute__((preserve_access_index));
-struct dentry {
-   struct inode *d_inode;
-} __attribute__((preserve_access_index));
-struct file {
-   struct inode *f_inode;
-} __attribute__((preserve_access_index));
-
-
 SEC("lsm/inode_unlink")
 int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
 {
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v3 5/9] bpf: Allow LSM programs to use bpf spin locks

2020-11-04 Thread KP Singh
From: KP Singh 

Usage of spin locks was not allowed for tracing programs due to
insufficient preemption checks. The verifier does not currently prevent
LSM programs from using spin locks, but the helpers are not exposed
via bpf_lsm_func_proto.

Based on the discussion in [1], non-sleepable LSM programs should be
able to use bpf_spin_{lock, unlock}.

Sleepable LSM programs can be preempted which means that allowng spin
locks will need more work (disabling preemption and the verifier
ensuring that no sleepable helpers are called when a spin lock is held).

[1]: 
https://lore.kernel.org/bpf/20201103153132.2717326-1-kpsi...@chromium.org/T/#md601a053229287659071600d3483523f752cd2fb

Signed-off-by: KP Singh 
---
 kernel/bpf/bpf_lsm.c  |  4 
 kernel/bpf/verifier.c | 17 +
 2 files changed, 21 insertions(+)

diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 61f8cc52fd5b..93383df2140b 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -63,6 +63,10 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct 
bpf_prog *prog)
return _task_storage_get_proto;
case BPF_FUNC_task_storage_delete:
return _task_storage_delete_proto;
+   case BPF_FUNC_spin_lock:
+   return _spin_lock_proto;
+   case BPF_FUNC_spin_unlock:
+   return _spin_unlock_proto;
default:
return tracing_prog_func_proto(func_id, prog);
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 314018e8fc12..7c6c246077cf 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9739,6 +9739,23 @@ static int check_map_prog_compatibility(struct 
bpf_verifier_env *env,
return -EINVAL;
}
 
+   if (map_value_has_spin_lock(map)) {
+   if (prog_type == BPF_PROG_TYPE_SOCKET_FILTER) {
+   verbose(env, "socket filter progs cannot use 
bpf_spin_lock yet\n");
+   return -EINVAL;
+   }
+
+   if (is_tracing_prog_type(prog_type)) {
+   verbose(env, "tracing progs cannot use bpf_spin_lock 
yet\n");
+   return -EINVAL;
+   }
+
+   if (prog->aux->sleepable) {
+   verbose(env, "sleepable progs cannot use bpf_spin_lock 
yet\n");
+   return -EINVAL;
+   }
+   }
+
if ((bpf_prog_is_dev_bound(prog->aux) || bpf_map_is_dev_bound(map)) &&
!bpf_offload_prog_map_match(prog, map)) {
verbose(env, "offload device mismatch between prog and map\n");
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v3 2/9] libbpf: Add support for task local storage

2020-11-04 Thread KP Singh
From: KP Singh 

Updates the bpf_probe_map_type API to also support
BPF_MAP_TYPE_TASK_STORAGE similar to other local storage maps.

Signed-off-by: KP Singh 
---
 tools/lib/bpf/libbpf_probes.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index 5482a9b7ae2d..ecaae2927ab8 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -230,6 +230,7 @@ bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 
ifindex)
break;
case BPF_MAP_TYPE_SK_STORAGE:
case BPF_MAP_TYPE_INODE_STORAGE:
+   case BPF_MAP_TYPE_TASK_STORAGE:
btf_key_type_id = 1;
btf_value_type_id = 3;
value_size = 8;
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v3 1/9] bpf: Implement task local storage

2020-11-04 Thread KP Singh
From: KP Singh 

Similar to bpf_local_storage for sockets and inodes add local storage
for task_struct.

The life-cycle of storage is managed with the life-cycle of the
task_struct.  i.e. the storage is destroyed along with the owning task
with a callback to the bpf_task_storage_free from the task_free LSM
hook.

The BPF LSM allocates an __rcu pointer to the bpf_local_storage in
the security blob which are now stackable and can co-exist with other
LSMs.

The userspace map operations can be done by using a pid fd as a key
passed to the lookup, update and delete operations.

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 include/linux/bpf_lsm.h|  23 +++
 include/linux/bpf_types.h  |   1 +
 include/uapi/linux/bpf.h   |  39 
 kernel/bpf/Makefile|   1 +
 kernel/bpf/bpf_lsm.c   |   4 +
 kernel/bpf/bpf_task_storage.c  | 313 +
 kernel/bpf/syscall.c   |   3 +-
 kernel/bpf/verifier.c  |  10 ++
 security/bpf/hooks.c   |   2 +
 tools/include/uapi/linux/bpf.h |  39 
 10 files changed, 434 insertions(+), 1 deletion(-)
 create mode 100644 kernel/bpf/bpf_task_storage.c

diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index aaacb6aafc87..73226181b744 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -7,6 +7,7 @@
 #ifndef _LINUX_BPF_LSM_H
 #define _LINUX_BPF_LSM_H
 
+#include 
 #include 
 #include 
 
@@ -35,9 +36,21 @@ static inline struct bpf_storage_blob *bpf_inode(
return inode->i_security + bpf_lsm_blob_sizes.lbs_inode;
 }
 
+static inline struct bpf_storage_blob *bpf_task(
+   const struct task_struct *task)
+{
+   if (unlikely(!task->security))
+   return NULL;
+
+   return task->security + bpf_lsm_blob_sizes.lbs_task;
+}
+
 extern const struct bpf_func_proto bpf_inode_storage_get_proto;
 extern const struct bpf_func_proto bpf_inode_storage_delete_proto;
+extern const struct bpf_func_proto bpf_task_storage_get_proto;
+extern const struct bpf_func_proto bpf_task_storage_delete_proto;
 void bpf_inode_storage_free(struct inode *inode);
+void bpf_task_storage_free(struct task_struct *task);
 
 #else /* !CONFIG_BPF_LSM */
 
@@ -53,10 +66,20 @@ static inline struct bpf_storage_blob *bpf_inode(
return NULL;
 }
 
+static inline struct bpf_storage_blob *bpf_task(
+   const struct task_struct *task)
+{
+   return NULL;
+}
+
 static inline void bpf_inode_storage_free(struct inode *inode)
 {
 }
 
+static inline void bpf_task_storage_free(struct task_struct *task)
+{
+}
+
 #endif /* CONFIG_BPF_LSM */
 
 #endif /* _LINUX_BPF_LSM_H */
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 2e6f568377f1..99f7fd657d87 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -109,6 +109,7 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKHASH, sock_hash_ops)
 #endif
 #ifdef CONFIG_BPF_LSM
 BPF_MAP_TYPE(BPF_MAP_TYPE_INODE_STORAGE, inode_storage_map_ops)
+BPF_MAP_TYPE(BPF_MAP_TYPE_TASK_STORAGE, task_storage_map_ops)
 #endif
 BPF_MAP_TYPE(BPF_MAP_TYPE_CPUMAP, cpu_map_ops)
 #if defined(CONFIG_XDP_SOCKETS)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index e6ceac3f7d62..f4037b2161a6 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -157,6 +157,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_STRUCT_OPS,
BPF_MAP_TYPE_RINGBUF,
BPF_MAP_TYPE_INODE_STORAGE,
+   BPF_MAP_TYPE_TASK_STORAGE,
 };
 
 /* Note that tracing related programs such as
@@ -3742,6 +3743,42 @@ union bpf_attr {
  * Return
  * The helper returns **TC_ACT_REDIRECT** on success or
  * **TC_ACT_SHOT** on error.
+ *
+ * void *bpf_task_storage_get(struct bpf_map *map, struct task_struct *task, 
void *value, u64 flags)
+ * Description
+ * Get a bpf_local_storage from the *task*.
+ *
+ * Logically, it could be thought of as getting the value from
+ * a *map* with *task* as the **key**.  From this
+ * perspective,  the usage is not much different from
+ * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this
+ * helper enforces the key must be an task_struct and the map must 
also
+ * be a **BPF_MAP_TYPE_TASK_STORAGE**.
+ *
+ * Underneath, the value is stored locally at *task* instead of
+ * the *map*.  The *map* is used as the bpf-local-storage
+ * "type". The bpf-local-storage "type" (i.e. the *map*) is
+ * searched against all bpf_local_storage residing at *task*.
+ *
+ * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be
+ * used such that a new bpf_local_storage will be
+ * created if one does not exist.  *value* can be used
+ * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify
+ * the initial value of a bpf_local_storage.  If *value* is

[PATCH bpf-next v3 8/9] bpf: Add tests for task_local_storage

2020-11-04 Thread KP Singh
From: KP Singh 

The test exercises the syscall based map operations by creating a pidfd
for the current process.

For verifying kernel / LSM functionality, the test implements a simple
MAC policy which denies an executable from unlinking itself. The LSM
program bprm_committed_creds sets a task_local_storage with a pointer to
the inode. This is then used to detect if the task is trying to unlink
itself in the inode_unlink LSM hook.

The test copies /bin/rm to /tmp and executes it in a child thread with
the intention of deleting itself. A successful test should prevent the
the running executable from deleting itself.

The bpf programs are also updated to call bpf_spin_{lock, unlock} to
trigger the verfier checks for spin locks.

The temporary file is cleaned up later in the test.

Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c   | 167 --
 .../selftests/bpf/progs/local_storage.c   |  61 ++-
 2 files changed, 210 insertions(+), 18 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
index 91cd6f357246..feba23f8848b 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -4,30 +4,149 @@
  * Copyright (C) 2020 Google LLC.
  */
 
+#define _GNU_SOURCE
+
+#include 
+#include 
+#include 
 #include 
 #include 
 
 #include "local_storage.skel.h"
 #include "network_helpers.h"
 
-int create_and_unlink_file(void)
+static inline int sys_pidfd_open(pid_t pid, unsigned int flags)
+{
+   return syscall(__NR_pidfd_open, pid, flags);
+}
+
+unsigned int duration;
+
+#define TEST_STORAGE_VALUE 0xbeefdead
+
+struct storage {
+   void *inode;
+   unsigned int value;
+   /* Lock ensures that spin locked versions of local stoage operations
+* also work, most operations in this tests are still single threaded
+*/
+   struct bpf_spin_lock lock;
+};
+
+/* Copies an rm binary to a temp file. dest is a mkstemp template */
+int copy_rm(char *dest)
 {
-   char fname[PATH_MAX] = "/tmp/fileXX";
-   int fd;
+   int ret, fd_in, fd_out;
+   struct stat stat;
 
-   fd = mkstemp(fname);
-   if (fd < 0)
-   return fd;
+   fd_in = open("/bin/rm", O_RDONLY);
+   if (fd_in < 0)
+   return fd_in;
 
-   close(fd);
-   unlink(fname);
+   fd_out = mkstemp(dest);
+   if (fd_out < 0)
+   return fd_out;
+
+   ret = fstat(fd_in, );
+   if (ret == -1)
+   return errno;
+
+   ret = copy_file_range(fd_in, NULL, fd_out, NULL, stat.st_size, 0);
+   if (ret == -1)
+   return errno;
+
+   /* Set executable permission on the copied file */
+   ret = chmod(dest, 0100);
+   if (ret == -1)
+   return errno;
+
+   close(fd_in);
+   close(fd_out);
return 0;
 }
 
+/* Fork and exec the provided rm binary and return the exit code of the
+ * forked process and its pid.
+ */
+int run_self_unlink(int *monitored_pid, const char *rm_path)
+{
+   int child_pid, child_status, ret;
+   int null_fd;
+
+   child_pid = fork();
+   if (child_pid == 0) {
+   null_fd = open("/dev/null", O_WRONLY);
+   dup2(null_fd, STDOUT_FILENO);
+   dup2(null_fd, STDERR_FILENO);
+   close(null_fd);
+
+   *monitored_pid = getpid();
+   /* Use the copied /usr/bin/rm to delete itself
+* /tmp/copy_of_rm /tmp/copy_of_rm.
+*/
+   ret = execlp(rm_path, rm_path, rm_path, NULL);
+   if (ret)
+   exit(errno);
+   } else if (child_pid > 0) {
+   waitpid(child_pid, _status, 0);
+   return WEXITSTATUS(child_status);
+   }
+
+   return -EINVAL;
+}
+
+bool check_syscall_operations(int map_fd, int obj_fd)
+{
+   struct storage val = { .value = TEST_STORAGE_VALUE, .lock = { 0 } },
+  lookup_val = { .value = 0, .lock = { 0 } };
+   int err;
+
+   /* Looking up an existing element should fail initially */
+   err = bpf_map_lookup_elem_flags(map_fd, _fd, _val,
+   BPF_F_LOCK);
+   if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem",
+ "err:%d errno:%d\n", err, errno))
+   return false;
+
+   /* Create a new element */
+   err = bpf_map_update_elem(map_fd, _fd, ,
+ BPF_NOEXIST | BPF_F_LOCK);
+   if (CHECK(err < 0, "bpf_map_update_elem", "err:%d errno:%d\n", err,
+ errno))
+   return false;
+
+   /* Lookup the newly created element */
+   err = bpf_map_lookup_elem_flags(map_fd, _fd, _val,
+ 

Re: [PATCH bpf-next v3 5/9] bpf: Allow LSM programs to use bpf spin locks

2020-11-04 Thread KP Singh
On Wed, Nov 4, 2020 at 11:35 PM Martin KaFai Lau  wrote:
>
> On Wed, Nov 04, 2020 at 05:44:49PM +0100, KP Singh wrote:
> > From: KP Singh 
> >
> > Usage of spin locks was not allowed for tracing programs due to
> > insufficient preemption checks. The verifier does not currently prevent
> > LSM programs from using spin locks, but the helpers are not exposed
> > via bpf_lsm_func_proto.
> This could be the first patch but don't feel strongly about it.
>
> >
> > Based on the discussion in [1], non-sleepable LSM programs should be
> > able to use bpf_spin_{lock, unlock}.
> >
> > Sleepable LSM programs can be preempted which means that allowng spin
> > locks will need more work (disabling preemption and the verifier
> > ensuring that no sleepable helpers are called when a spin lock is held).
> >
> > [1]: 
> > https://lore.kernel.org/bpf/20201103153132.2717326-1-kpsi...@chromium.org/T/#md601a053229287659071600d3483523f752cd2fb
> >
> > Signed-off-by: KP Singh 
> > ---
> >  kernel/bpf/bpf_lsm.c  |  4 
> >  kernel/bpf/verifier.c | 17 +
> >  2 files changed, 21 insertions(+)
> >
> > diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
> > index 61f8cc52fd5b..93383df2140b 100644
> > --- a/kernel/bpf/bpf_lsm.c
> > +++ b/kernel/bpf/bpf_lsm.c
> > @@ -63,6 +63,10 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const 
> > struct bpf_prog *prog)
> >   return _task_storage_get_proto;
> >   case BPF_FUNC_task_storage_delete:
> >   return _task_storage_delete_proto;
> > + case BPF_FUNC_spin_lock:
> > + return _spin_lock_proto;
> > + case BPF_FUNC_spin_unlock:
> > + return _spin_unlock_proto;
> >   default:
> >   return tracing_prog_func_proto(func_id, prog);
> >   }
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index 314018e8fc12..7c6c246077cf 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -9739,6 +9739,23 @@ static int check_map_prog_compatibility(struct 
> > bpf_verifier_env *env,
> >   return -EINVAL;
> >   }
> >
> > + if (map_value_has_spin_lock(map)) {
> > + if (prog_type == BPF_PROG_TYPE_SOCKET_FILTER) {
> > + verbose(env, "socket filter progs cannot use 
> > bpf_spin_lock yet\n");
> > + return -EINVAL;
> > + }
> > +
> > + if (is_tracing_prog_type(prog_type)) {
> > + verbose(env, "tracing progs cannot use bpf_spin_lock 
> > yet\n");
> > + return -EINVAL;
> > + }
> It is good to have a more specific verifier log.  However,
> these are duplicated checks (a few lines above in the same function).
> They should at least be removed.
>

Thanks, I fixed this up and will move this as the first patch.

> > +
> > + if (prog->aux->sleepable) {
> > + verbose(env, "sleepable progs cannot use 
> > bpf_spin_lock yet\n");
> > + return -EINVAL;
> > + }
> > + }
> > +
> >   if ((bpf_prog_is_dev_bound(prog->aux) || bpf_map_is_dev_bound(map)) &&
> >   !bpf_offload_prog_map_match(prog, map)) {
> >   verbose(env, "offload device mismatch between prog and 
> > map\n");
> > --
> > 2.29.1.341.ge80a0c044ae-goog
> >


Re: [PATCH bpf-next v3 1/9] bpf: Implement task local storage

2020-11-04 Thread KP Singh
> > + WARN_ON_ONCE(!rcu_read_lock_held());
> > + task = pid_task(pid, PIDTYPE_PID);
> > + if (!task) {
> > + err = -ENOENT;
> > + goto out;
> > + }
> > +
> > + sdata = bpf_local_storage_update(
> > + task, (struct bpf_local_storage_map *)map, value, map_flags);
> It seems the task is protected by rcu here and the task may be going away.
> Is it ok?
>
> or the following comment in the later "BPF_CALL_4(bpf_task_storage_get, ...)"
> is no longer valid?
> /* This helper must only called from where the task is guaranteed
>  * to have a refcount and cannot be freed.
>  */

I reworded this (and the other similar comment) as:

/* This helper must only be called from places where the lifetime of the task
* is guaranteed. Either by being refcounted or by being protected
* by an RCU read-side critical section.
*/


[PATCH bpf-next v4 7/9] bpf: Update selftests for local_storage to use vmlinux.h

2020-11-05 Thread KP Singh
From: KP Singh 

With the fixing of BTF pruning of embedded types being fixed, the test
can be simplified to use vmlinux.h

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 .../selftests/bpf/progs/local_storage.c   | 20 +--
 1 file changed, 1 insertion(+), 19 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
index 09529e33be98..ef3822bc7542 100644
--- a/tools/testing/selftests/bpf/progs/local_storage.c
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -4,9 +4,8 @@
  * Copyright 2020 Google LLC.
  */
 
+#include "vmlinux.h"
 #include 
-#include 
-#include 
 #include 
 #include 
 
@@ -36,23 +35,6 @@ struct {
__type(value, struct dummy_storage);
 } sk_storage_map SEC(".maps");
 
-/* TODO Use vmlinux.h once BTF pruning for embedded types is fixed.
- */
-struct sock {} __attribute__((preserve_access_index));
-struct sockaddr {} __attribute__((preserve_access_index));
-struct socket {
-   struct sock *sk;
-} __attribute__((preserve_access_index));
-
-struct inode {} __attribute__((preserve_access_index));
-struct dentry {
-   struct inode *d_inode;
-} __attribute__((preserve_access_index));
-struct file {
-   struct inode *f_inode;
-} __attribute__((preserve_access_index));
-
-
 SEC("lsm/inode_unlink")
 int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
 {
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v4 0/9] Implement task_local_storage

2020-11-05 Thread KP Singh
From: KP Singh 

# v3 -> v4

- Move the patch that exposes spin lock helpers to LSM programs as the
  first patch as some of the changes in the implementation are actually
  for spin locks.
- Clarify the comment in the bpf_task_storage_{get, delete} helper as
  discussed with Martin.
- Added Martin's ack and rebased.

# v2 -> v3

- Added bpf_spin_locks to the selftests for local storage, found that
  these are not available for LSM programs.
- Made spin lock helpers available for LSM programs (except sleepable
  programs which need more work).
- Minor fixes for includes and added short commit messages for patches
  that were split up for libbpf and bpftool.
- Added Song's acks.

# v1 -> v2

- Updated the refcounting for task_struct and simplified conversion
  of fd -> struct pid.
- Some fixes suggested by Martin and Andrii, notably:
   * long return type for the bpf_task_storage_delete helper (update
 for bpf_inode_storage_delete will be sent separately).
   * Remove extra nullness check to task_storage_ptr in map syscall
 ops.
   * Changed the argument signature of the BPF helpers to use
 task_struct pointer in uapi headers.
   * Remove unnecessary verifier logic for the bpf_get_current_task_btf
 helper.
   * Split the changes for bpftool and libbpf.
- Exercised syscall operations for local storage (kept a simpler verison
  in test_local_storage.c, the eventual goal will be to update
  sk_storage_map.c for all local storage types).
- Formatting fixes + Rebase.

We already have socket and inode local storage since [1]

This patch series:

* Implements bpf_local_storage for task_struct.
* Implements the bpf_get_current_task_btf helper which returns a BTF
  pointer to the current task. Not only is this generally cleaner
  (reading from the task_struct currently requires BPF_CORE_READ), it
  also allows the BTF pointer to be used in task_local_storage helpers.
* In order to implement this helper, a RET_PTR_TO_BTF_ID is introduced
  which works similar to RET_PTR_TO_BTF_ID_OR_NULL but does not require
  a nullness check.
* Implements a detection in selftests which uses the
  task local storage to deny a running executable from unlinking itself.

[1]: 
https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=f836a56e84ffc9f1a1cd73f77e10404ca46a4616

KP Singh (9):
  bpf: Allow LSM programs to use bpf spin locks
  bpf: Implement task local storage
  libbpf: Add support for task local storage
  bpftool: Add support for task local storage
  bpf: Implement get_current_task_btf and RET_PTR_TO_BTF_ID
  bpf: Fix tests for local_storage
  bpf: Update selftests for local_storage to use vmlinux.h
  bpf: Add tests for task_local_storage
  bpf: Exercise syscall operations for inode and sk storage

 include/linux/bpf.h   |   1 +
 include/linux/bpf_lsm.h   |  23 ++
 include/linux/bpf_types.h |   1 +
 include/uapi/linux/bpf.h  |  48 +++
 kernel/bpf/Makefile   |   1 +
 kernel/bpf/bpf_lsm.c  |   8 +
 kernel/bpf/bpf_task_storage.c | 315 ++
 kernel/bpf/syscall.c  |   3 +-
 kernel/bpf/verifier.c |  37 +-
 kernel/trace/bpf_trace.c  |  16 +
 security/bpf/hooks.c  |   2 +
 .../bpf/bpftool/Documentation/bpftool-map.rst |   3 +-
 tools/bpf/bpftool/bash-completion/bpftool |   2 +-
 tools/bpf/bpftool/map.c   |   4 +-
 tools/include/uapi/linux/bpf.h|  48 +++
 tools/lib/bpf/libbpf_probes.c |   1 +
 .../bpf/prog_tests/test_local_storage.c   | 182 +-
 .../selftests/bpf/progs/local_storage.c   | 103 --
 18 files changed, 741 insertions(+), 57 deletions(-)
 create mode 100644 kernel/bpf/bpf_task_storage.c

-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v4 3/9] libbpf: Add support for task local storage

2020-11-05 Thread KP Singh
From: KP Singh 

Updates the bpf_probe_map_type API to also support
BPF_MAP_TYPE_TASK_STORAGE similar to other local storage maps.

Acked-by: Martin KaFai Lau 
Signed-off-by: KP Singh 
---
 tools/lib/bpf/libbpf_probes.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index 5482a9b7ae2d..ecaae2927ab8 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -230,6 +230,7 @@ bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 
ifindex)
break;
case BPF_MAP_TYPE_SK_STORAGE:
case BPF_MAP_TYPE_INODE_STORAGE:
+   case BPF_MAP_TYPE_TASK_STORAGE:
btf_key_type_id = 1;
btf_value_type_id = 3;
value_size = 8;
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v4 9/9] bpf: Exercise syscall operations for inode and sk storage

2020-11-05 Thread KP Singh
From: KP Singh 

Use the check_syscall_operations added for task_local_storage to
exercise syscall operations for other local storage maps:

* Check the absence of an element for the given fd.
* Create a new element, retrieve and compare its value.
* Delete the element and check again for absence.

Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c | 17 +++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
index feba23f8848b..48c9237f1314 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -145,7 +145,7 @@ bool check_syscall_operations(int map_fd, int obj_fd)
 void test_test_local_storage(void)
 {
char tmp_exec_path[PATH_MAX] = "/tmp/copy_of_rmXX";
-   int err, serv_sk = -1, task_fd = -1;
+   int err, serv_sk = -1, task_fd = -1, rm_fd = -1;
struct local_storage *skel = NULL;
 
skel = local_storage__open_and_load();
@@ -169,6 +169,15 @@ void test_test_local_storage(void)
if (CHECK(err < 0, "copy_rm", "err %d errno %d\n", err, errno))
goto close_prog;
 
+   rm_fd = open(tmp_exec_path, O_RDONLY);
+   if (CHECK(rm_fd < 0, "open", "failed to open %s err:%d, errno:%d",
+ tmp_exec_path, rm_fd, errno))
+   goto close_prog;
+
+   if (!check_syscall_operations(bpf_map__fd(skel->maps.inode_storage_map),
+ rm_fd))
+   goto close_prog;
+
/* Sets skel->bss->monitored_pid to the pid of the forked child
 * forks a child process that executes tmp_exec_path and tries to
 * unlink its executable. This operation should be denied by the loaded
@@ -197,9 +206,13 @@ void test_test_local_storage(void)
CHECK(skel->data->sk_storage_result != 0, "sk_storage_result",
  "sk_local_storage not set\n");
 
-   close(serv_sk);
+   if (!check_syscall_operations(bpf_map__fd(skel->maps.sk_storage_map),
+ serv_sk))
+   goto close_prog;
 
 close_prog:
+   close(serv_sk);
+   close(rm_fd);
close(task_fd);
local_storage__destroy(skel);
 }
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v4 8/9] bpf: Add tests for task_local_storage

2020-11-05 Thread KP Singh
From: KP Singh 

The test exercises the syscall based map operations by creating a pidfd
for the current process.

For verifying kernel / LSM functionality, the test implements a simple
MAC policy which denies an executable from unlinking itself. The LSM
program bprm_committed_creds sets a task_local_storage with a pointer to
the inode. This is then used to detect if the task is trying to unlink
itself in the inode_unlink LSM hook.

The test copies /bin/rm to /tmp and executes it in a child thread with
the intention of deleting itself. A successful test should prevent the
the running executable from deleting itself.

The bpf programs are also updated to call bpf_spin_{lock, unlock} to
trigger the verfier checks for spin locks.

The temporary file is cleaned up later in the test.

Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c   | 167 --
 .../selftests/bpf/progs/local_storage.c   |  61 ++-
 2 files changed, 210 insertions(+), 18 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
index 91cd6f357246..feba23f8848b 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -4,30 +4,149 @@
  * Copyright (C) 2020 Google LLC.
  */
 
+#define _GNU_SOURCE
+
+#include 
+#include 
+#include 
 #include 
 #include 
 
 #include "local_storage.skel.h"
 #include "network_helpers.h"
 
-int create_and_unlink_file(void)
+static inline int sys_pidfd_open(pid_t pid, unsigned int flags)
+{
+   return syscall(__NR_pidfd_open, pid, flags);
+}
+
+unsigned int duration;
+
+#define TEST_STORAGE_VALUE 0xbeefdead
+
+struct storage {
+   void *inode;
+   unsigned int value;
+   /* Lock ensures that spin locked versions of local stoage operations
+* also work, most operations in this tests are still single threaded
+*/
+   struct bpf_spin_lock lock;
+};
+
+/* Copies an rm binary to a temp file. dest is a mkstemp template */
+int copy_rm(char *dest)
 {
-   char fname[PATH_MAX] = "/tmp/fileXX";
-   int fd;
+   int ret, fd_in, fd_out;
+   struct stat stat;
 
-   fd = mkstemp(fname);
-   if (fd < 0)
-   return fd;
+   fd_in = open("/bin/rm", O_RDONLY);
+   if (fd_in < 0)
+   return fd_in;
 
-   close(fd);
-   unlink(fname);
+   fd_out = mkstemp(dest);
+   if (fd_out < 0)
+   return fd_out;
+
+   ret = fstat(fd_in, );
+   if (ret == -1)
+   return errno;
+
+   ret = copy_file_range(fd_in, NULL, fd_out, NULL, stat.st_size, 0);
+   if (ret == -1)
+   return errno;
+
+   /* Set executable permission on the copied file */
+   ret = chmod(dest, 0100);
+   if (ret == -1)
+   return errno;
+
+   close(fd_in);
+   close(fd_out);
return 0;
 }
 
+/* Fork and exec the provided rm binary and return the exit code of the
+ * forked process and its pid.
+ */
+int run_self_unlink(int *monitored_pid, const char *rm_path)
+{
+   int child_pid, child_status, ret;
+   int null_fd;
+
+   child_pid = fork();
+   if (child_pid == 0) {
+   null_fd = open("/dev/null", O_WRONLY);
+   dup2(null_fd, STDOUT_FILENO);
+   dup2(null_fd, STDERR_FILENO);
+   close(null_fd);
+
+   *monitored_pid = getpid();
+   /* Use the copied /usr/bin/rm to delete itself
+* /tmp/copy_of_rm /tmp/copy_of_rm.
+*/
+   ret = execlp(rm_path, rm_path, rm_path, NULL);
+   if (ret)
+   exit(errno);
+   } else if (child_pid > 0) {
+   waitpid(child_pid, _status, 0);
+   return WEXITSTATUS(child_status);
+   }
+
+   return -EINVAL;
+}
+
+bool check_syscall_operations(int map_fd, int obj_fd)
+{
+   struct storage val = { .value = TEST_STORAGE_VALUE, .lock = { 0 } },
+  lookup_val = { .value = 0, .lock = { 0 } };
+   int err;
+
+   /* Looking up an existing element should fail initially */
+   err = bpf_map_lookup_elem_flags(map_fd, _fd, _val,
+   BPF_F_LOCK);
+   if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem",
+ "err:%d errno:%d\n", err, errno))
+   return false;
+
+   /* Create a new element */
+   err = bpf_map_update_elem(map_fd, _fd, ,
+ BPF_NOEXIST | BPF_F_LOCK);
+   if (CHECK(err < 0, "bpf_map_update_elem", "err:%d errno:%d\n", err,
+ errno))
+   return false;
+
+   /* Lookup the newly created element */
+   err = bpf_map_lookup_elem_flags(map_fd, _fd, _val,
+ 

[PATCH bpf-next v4 5/9] bpf: Implement get_current_task_btf and RET_PTR_TO_BTF_ID

2020-11-05 Thread KP Singh
From: KP Singh 

The currently available bpf_get_current_task returns an unsigned integer
which can be used along with BPF_CORE_READ to read data from
the task_struct but still cannot be used as an input argument to a
helper that accepts an ARG_PTR_TO_BTF_ID of type task_struct.

In order to implement this helper a new return type, RET_PTR_TO_BTF_ID,
is added. This is similar to RET_PTR_TO_BTF_ID_OR_NULL but does not
require checking the nullness of returned pointer.

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 include/linux/bpf.h|  1 +
 include/uapi/linux/bpf.h   |  9 +
 kernel/bpf/verifier.c  |  7 +--
 kernel/trace/bpf_trace.c   | 16 
 tools/include/uapi/linux/bpf.h |  9 +
 5 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2fffd30e13ac..73d5381a5d5c 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -310,6 +310,7 @@ enum bpf_return_type {
RET_PTR_TO_BTF_ID_OR_NULL,  /* returns a pointer to a btf_id or 
NULL */
RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL, /* returns a pointer to a valid 
memory or a btf_id or NULL */
RET_PTR_TO_MEM_OR_BTF_ID,   /* returns a pointer to a valid memory 
or a btf_id */
+   RET_PTR_TO_BTF_ID,  /* returns a pointer to a btf_id */
 };
 
 /* eBPF function prototype used by verifier to allow BPF_CALLs from eBPF 
programs
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index f4037b2161a6..9879d6793e90 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -3779,6 +3779,14 @@ union bpf_attr {
  * 0 on success.
  *
  * **-ENOENT** if the bpf_local_storage cannot be found.
+ *
+ * struct task_struct *bpf_get_current_task_btf(void)
+ * Description
+ * Return a BTF pointer to the "current" task.
+ * This pointer can also be used in helpers that accept an
+ * *ARG_PTR_TO_BTF_ID* of type *task_struct*.
+ * Return
+ * Pointer to the current task.
  */
 #define __BPF_FUNC_MAPPER(FN)  \
FN(unspec), \
@@ -3939,6 +3947,7 @@ union bpf_attr {
FN(redirect_peer),  \
FN(task_storage_get),   \
FN(task_storage_delete),\
+   FN(get_current_task_btf),   \
/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 00960f6a83ec..10da26e55130 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5186,11 +5186,14 @@ static int check_helper_call(struct bpf_verifier_env 
*env, int func_id, int insn
PTR_TO_BTF_ID : PTR_TO_BTF_ID_OR_NULL;
regs[BPF_REG_0].btf_id = meta.ret_btf_id;
}
-   } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL) {
+   } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL ||
+  fn->ret_type == RET_PTR_TO_BTF_ID) {
int ret_btf_id;
 
mark_reg_known_zero(env, regs, BPF_REG_0);
-   regs[BPF_REG_0].type = PTR_TO_BTF_ID_OR_NULL;
+   regs[BPF_REG_0].type = fn->ret_type == RET_PTR_TO_BTF_ID ?
+PTR_TO_BTF_ID :
+PTR_TO_BTF_ID_OR_NULL;
ret_btf_id = *fn->ret_btf_id;
if (ret_btf_id == 0) {
verbose(env, "invalid return type %d of func %s#%d\n",
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 4517c8b66518..e4515b0f62a8 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1022,6 +1022,20 @@ const struct bpf_func_proto bpf_get_current_task_proto = 
{
.ret_type   = RET_INTEGER,
 };
 
+BPF_CALL_0(bpf_get_current_task_btf)
+{
+   return (unsigned long) current;
+}
+
+BTF_ID_LIST_SINGLE(bpf_get_current_btf_ids, struct, task_struct)
+
+static const struct bpf_func_proto bpf_get_current_task_btf_proto = {
+   .func   = bpf_get_current_task_btf,
+   .gpl_only   = true,
+   .ret_type   = RET_PTR_TO_BTF_ID,
+   .ret_btf_id = _get_current_btf_ids[0],
+};
+
 BPF_CALL_2(bpf_current_task_under_cgroup, struct bpf_map *, map, u32, idx)
 {
struct bpf_array *array = container_of(map, struct bpf_array, map);
@@ -1265,6 +1279,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const 
struct bpf_prog *prog)
return _get_current_pid_tgid_proto;
case BPF_FUNC_get_current_task:
return _get_current_task_proto;
+   case BPF_FUNC_get_current_task_btf:
+   return _get_current_task_btf_proto;
case BPF_FUNC_get_current_uid_gid:
return _get_current_uid_gid_proto;
case BPF_FUNC_get_current_comm:
diff --git

[PATCH bpf-next v4 1/9] bpf: Allow LSM programs to use bpf spin locks

2020-11-05 Thread KP Singh
From: KP Singh 

Usage of spin locks was not allowed for tracing programs due to
insufficient preemption checks. The verifier does not currently prevent
LSM programs from using spin locks, but the helpers are not exposed
via bpf_lsm_func_proto.

Based on the discussion in [1], non-sleepable LSM programs should be
able to use bpf_spin_{lock, unlock}.

Sleepable LSM programs can be preempted which means that allowng spin
locks will need more work (disabling preemption and the verifier
ensuring that no sleepable helpers are called when a spin lock is held).

[1]: 
https://lore.kernel.org/bpf/20201103153132.2717326-1-kpsi...@chromium.org/T/#md601a053229287659071600d3483523f752cd2fb

Signed-off-by: KP Singh 
---
 kernel/bpf/bpf_lsm.c  |  4 
 kernel/bpf/verifier.c | 20 +++-
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 78ea8a7bd27f..cd8a617f2109 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -59,6 +59,10 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct 
bpf_prog *prog)
return _sk_storage_get_proto;
case BPF_FUNC_sk_storage_delete:
return _sk_storage_delete_proto;
+   case BPF_FUNC_spin_lock:
+   return _spin_lock_proto;
+   case BPF_FUNC_spin_unlock:
+   return _spin_unlock_proto;
default:
return tracing_prog_func_proto(func_id, prog);
}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6200519582a6..f863aa84d0a2 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9719,11 +9719,21 @@ static int check_map_prog_compatibility(struct 
bpf_verifier_env *env,
verbose(env, "trace type programs with run-time allocated hash 
maps are unsafe. Switch to preallocated hash maps.\n");
}
 
-   if ((is_tracing_prog_type(prog_type) ||
-prog_type == BPF_PROG_TYPE_SOCKET_FILTER) &&
-   map_value_has_spin_lock(map)) {
-   verbose(env, "tracing progs cannot use bpf_spin_lock yet\n");
-   return -EINVAL;
+   if (map_value_has_spin_lock(map)) {
+   if (prog_type == BPF_PROG_TYPE_SOCKET_FILTER) {
+   verbose(env, "socket filter progs cannot use 
bpf_spin_lock yet\n");
+   return -EINVAL;
+   }
+
+   if (is_tracing_prog_type(prog_type)) {
+   verbose(env, "tracing progs cannot use bpf_spin_lock 
yet\n");
+   return -EINVAL;
+   }
+
+   if (prog->aux->sleepable) {
+   verbose(env, "sleepable progs cannot use bpf_spin_lock 
yet\n");
+   return -EINVAL;
+   }
}
 
if ((bpf_prog_is_dev_bound(prog->aux) || bpf_map_is_dev_bound(map)) &&
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v4 2/9] bpf: Implement task local storage

2020-11-05 Thread KP Singh
From: KP Singh 

Similar to bpf_local_storage for sockets and inodes add local storage
for task_struct.

The life-cycle of storage is managed with the life-cycle of the
task_struct.  i.e. the storage is destroyed along with the owning task
with a callback to the bpf_task_storage_free from the task_free LSM
hook.

The BPF LSM allocates an __rcu pointer to the bpf_local_storage in
the security blob which are now stackable and can co-exist with other
LSMs.

The userspace map operations can be done by using a pid fd as a key
passed to the lookup, update and delete operations.

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 include/linux/bpf_lsm.h|  23 +++
 include/linux/bpf_types.h  |   1 +
 include/uapi/linux/bpf.h   |  39 
 kernel/bpf/Makefile|   1 +
 kernel/bpf/bpf_lsm.c   |   4 +
 kernel/bpf/bpf_task_storage.c  | 315 +
 kernel/bpf/syscall.c   |   3 +-
 kernel/bpf/verifier.c  |  10 ++
 security/bpf/hooks.c   |   2 +
 tools/include/uapi/linux/bpf.h |  39 
 10 files changed, 436 insertions(+), 1 deletion(-)
 create mode 100644 kernel/bpf/bpf_task_storage.c

diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index aaacb6aafc87..73226181b744 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -7,6 +7,7 @@
 #ifndef _LINUX_BPF_LSM_H
 #define _LINUX_BPF_LSM_H
 
+#include 
 #include 
 #include 
 
@@ -35,9 +36,21 @@ static inline struct bpf_storage_blob *bpf_inode(
return inode->i_security + bpf_lsm_blob_sizes.lbs_inode;
 }
 
+static inline struct bpf_storage_blob *bpf_task(
+   const struct task_struct *task)
+{
+   if (unlikely(!task->security))
+   return NULL;
+
+   return task->security + bpf_lsm_blob_sizes.lbs_task;
+}
+
 extern const struct bpf_func_proto bpf_inode_storage_get_proto;
 extern const struct bpf_func_proto bpf_inode_storage_delete_proto;
+extern const struct bpf_func_proto bpf_task_storage_get_proto;
+extern const struct bpf_func_proto bpf_task_storage_delete_proto;
 void bpf_inode_storage_free(struct inode *inode);
+void bpf_task_storage_free(struct task_struct *task);
 
 #else /* !CONFIG_BPF_LSM */
 
@@ -53,10 +66,20 @@ static inline struct bpf_storage_blob *bpf_inode(
return NULL;
 }
 
+static inline struct bpf_storage_blob *bpf_task(
+   const struct task_struct *task)
+{
+   return NULL;
+}
+
 static inline void bpf_inode_storage_free(struct inode *inode)
 {
 }
 
+static inline void bpf_task_storage_free(struct task_struct *task)
+{
+}
+
 #endif /* CONFIG_BPF_LSM */
 
 #endif /* _LINUX_BPF_LSM_H */
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 2e6f568377f1..99f7fd657d87 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -109,6 +109,7 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKHASH, sock_hash_ops)
 #endif
 #ifdef CONFIG_BPF_LSM
 BPF_MAP_TYPE(BPF_MAP_TYPE_INODE_STORAGE, inode_storage_map_ops)
+BPF_MAP_TYPE(BPF_MAP_TYPE_TASK_STORAGE, task_storage_map_ops)
 #endif
 BPF_MAP_TYPE(BPF_MAP_TYPE_CPUMAP, cpu_map_ops)
 #if defined(CONFIG_XDP_SOCKETS)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index e6ceac3f7d62..f4037b2161a6 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -157,6 +157,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_STRUCT_OPS,
BPF_MAP_TYPE_RINGBUF,
BPF_MAP_TYPE_INODE_STORAGE,
+   BPF_MAP_TYPE_TASK_STORAGE,
 };
 
 /* Note that tracing related programs such as
@@ -3742,6 +3743,42 @@ union bpf_attr {
  * Return
  * The helper returns **TC_ACT_REDIRECT** on success or
  * **TC_ACT_SHOT** on error.
+ *
+ * void *bpf_task_storage_get(struct bpf_map *map, struct task_struct *task, 
void *value, u64 flags)
+ * Description
+ * Get a bpf_local_storage from the *task*.
+ *
+ * Logically, it could be thought of as getting the value from
+ * a *map* with *task* as the **key**.  From this
+ * perspective,  the usage is not much different from
+ * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this
+ * helper enforces the key must be an task_struct and the map must 
also
+ * be a **BPF_MAP_TYPE_TASK_STORAGE**.
+ *
+ * Underneath, the value is stored locally at *task* instead of
+ * the *map*.  The *map* is used as the bpf-local-storage
+ * "type". The bpf-local-storage "type" (i.e. the *map*) is
+ * searched against all bpf_local_storage residing at *task*.
+ *
+ * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be
+ * used such that a new bpf_local_storage will be
+ * created if one does not exist.  *value* can be used
+ * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify
+ * the initial value of a bpf_local_storage.  If *value* is

[PATCH bpf-next v4 6/9] bpf: Fix tests for local_storage

2020-11-05 Thread KP Singh
From: KP Singh 

The {inode,sk}_storage_result checking if the correct value was retrieved
was being clobbered unconditionally by the return value of the
bpf_{inode,sk}_storage_delete call.

Also, consistently use the newly added BPF_LOCAL_STORAGE_GET_F_CREATE
flag.

Acked-by: Song Liu 
Fixes: cd324d7abb3d ("bpf: Add selftests for local_storage")
Signed-off-by: KP Singh 
---
 .../selftests/bpf/progs/local_storage.c   | 24 ---
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
index 0758ba229ae0..09529e33be98 100644
--- a/tools/testing/selftests/bpf/progs/local_storage.c
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -58,20 +58,22 @@ int BPF_PROG(unlink_hook, struct inode *dir, struct dentry 
*victim)
 {
__u32 pid = bpf_get_current_pid_tgid() >> 32;
struct dummy_storage *storage;
+   int err;
 
if (pid != monitored_pid)
return 0;
 
storage = bpf_inode_storage_get(_storage_map, victim->d_inode, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+   BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-   if (storage->value == DUMMY_STORAGE_VALUE)
+   if (storage->value != DUMMY_STORAGE_VALUE)
inode_storage_result = -1;
 
-   inode_storage_result =
-   bpf_inode_storage_delete(_storage_map, victim->d_inode);
+   err = bpf_inode_storage_delete(_storage_map, victim->d_inode);
+   if (!err)
+   inode_storage_result = err;
 
return 0;
 }
@@ -82,19 +84,23 @@ int BPF_PROG(socket_bind, struct socket *sock, struct 
sockaddr *address,
 {
__u32 pid = bpf_get_current_pid_tgid() >> 32;
struct dummy_storage *storage;
+   int err;
 
if (pid != monitored_pid)
return 0;
 
storage = bpf_sk_storage_get(_storage_map, sock->sk, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-   if (storage->value == DUMMY_STORAGE_VALUE)
+   if (storage->value != DUMMY_STORAGE_VALUE)
sk_storage_result = -1;
 
-   sk_storage_result = bpf_sk_storage_delete(_storage_map, sock->sk);
+   err = bpf_sk_storage_delete(_storage_map, sock->sk);
+   if (!err)
+   sk_storage_result = err;
+
return 0;
 }
 
@@ -109,7 +115,7 @@ int BPF_PROG(socket_post_create, struct socket *sock, int 
family, int type,
return 0;
 
storage = bpf_sk_storage_get(_storage_map, sock->sk, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
@@ -131,7 +137,7 @@ int BPF_PROG(file_open, struct file *file)
return 0;
 
storage = bpf_inode_storage_get(_storage_map, file->f_inode, 0,
-BPF_LOCAL_STORAGE_GET_F_CREATE);
+   BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v4 4/9] bpftool: Add support for task local storage

2020-11-05 Thread KP Singh
From: KP Singh 

Updates the binary to handle the BPF_MAP_TYPE_TASK_STORAGE as
"task_storage" for printing and parsing. Also updates the documentation
and bash completion

Acked-by: Song Liu 
Signed-off-by: KP Singh 
---
 tools/bpf/bpftool/Documentation/bpftool-map.rst | 3 ++-
 tools/bpf/bpftool/bash-completion/bpftool   | 2 +-
 tools/bpf/bpftool/map.c | 4 +++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst 
b/tools/bpf/bpftool/Documentation/bpftool-map.rst
index dade10cdf295..3d52256ba75f 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-map.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst
@@ -50,7 +50,8 @@ MAP COMMANDS
 |  | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | 
**hash_of_maps**
 |  | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | 
**xskmap** | **sockhash**
 |  | **cgroup_storage** | **reuseport_sockarray** | 
**percpu_cgroup_storage**
-|  | **queue** | **stack** | **sk_storage** | **struct_ops** | 
**ringbuf** | **inode_storage** }
+|  | **queue** | **stack** | **sk_storage** | **struct_ops** | 
**ringbuf** | **inode_storage**
+   | **task_storage** }
 
 DESCRIPTION
 ===
diff --git a/tools/bpf/bpftool/bash-completion/bpftool 
b/tools/bpf/bpftool/bash-completion/bpftool
index 3f1da30c4da6..fdffbc64c65c 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -705,7 +705,7 @@ _bpftool()
 hash_of_maps devmap devmap_hash sockmap cpumap 
\
 xskmap sockhash cgroup_storage 
reuseport_sockarray \
 percpu_cgroup_storage queue stack sk_storage \
-struct_ops inode_storage' -- \
+struct_ops inode_storage task_storage' -- \
"$cur" ) )
 return 0
 ;;
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index a7efbd84fbcc..b400364ee054 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -51,6 +51,7 @@ const char * const map_type_name[] = {
[BPF_MAP_TYPE_STRUCT_OPS]   = "struct_ops",
[BPF_MAP_TYPE_RINGBUF]  = "ringbuf",
[BPF_MAP_TYPE_INODE_STORAGE]= "inode_storage",
+   [BPF_MAP_TYPE_TASK_STORAGE] = "task_storage",
 };
 
 const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
@@ -1464,7 +1465,8 @@ static int do_help(int argc, char **argv)
" lru_percpu_hash | lpm_trie | array_of_maps | 
hash_of_maps |\n"
" devmap | devmap_hash | sockmap | cpumap | 
xskmap | sockhash |\n"
" cgroup_storage | reuseport_sockarray | 
percpu_cgroup_storage |\n"
-   " queue | stack | sk_storage | struct_ops | 
ringbuf | inode_storage }\n"
+   " queue | stack | sk_storage | struct_ops | 
ringbuf | inode_storage |\n"
+   " task_storage }\n"
"   " HELP_SPEC_OPTIONS "\n"
"",
bin_name, argv[-2]);
-- 
2.29.1.341.ge80a0c044ae-goog



Re: [PATCH bpf-next 1/5] bpf: Implement task local storage

2020-11-03 Thread KP Singh
On Fri, Oct 30, 2020 at 11:53 AM KP Singh  wrote:
>
> Thanks for taking a look!
>
> On Wed, Oct 28, 2020 at 2:13 AM Martin KaFai Lau  wrote:
> >
> > On Tue, Oct 27, 2020 at 06:03:13PM +0100, KP Singh wrote:
> > [ ... ]
> >
> > > diff --git a/kernel/bpf/bpf_task_storage.c b/kernel/bpf/bpf_task_storage.c
> > > new file mode 100644
> > > index ..774140c458cc
> > > --- /dev/null
> > > +++ b/kernel/bpf/bpf_task_storage.c
> > > @@ -0,0 +1,327 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Copyright (c) 2019 Facebook
> > > + * Copyright 2020 Google LLC.
> > > + */
> > > +
> > > +#include "linux/pid.h"
> > > +#include "linux/sched.h"
> > > +#include 
> > > +#include 
> > > +#include 
> > > +#include 
> > > +#include 
> > > +#include 
> > > +#include 
> > > +#include 
> > Is this required?
>
> Nope. Removed.
>
> >
> > > +#include 
> > > +#include 
> > > +#include 
> > > +#include 
> > > +#include 
> > > +
> > > +DEFINE_BPF_STORAGE_CACHE(task_cache);
> > > +
> > > +static struct bpf_local_storage __rcu **task_storage_ptr(void *owner)
>
> [...]
>
> > > + err = -EBADF;
> > > + goto out_fput;
> > > + }
> > > +
> > > + pid = get_pid(f->private_data);
> > n00b question. Is get_pid(f->private_data) required?
> > f->private_data could be freed while holding f->f_count?
>
> I would assume that holding a reference to the file should also
> keep the private_data alive but I was not sure so I grabbed the
> extra reference.
>
> >
> > > + task = get_pid_task(pid, PIDTYPE_PID);
> > Should put_task_struct() be called before returning?
>
> If we keep using get_pid_task then, yes, I see it grabs a reference to the 
> task.
> We could also call pid_task under rcu locks but it might be cleaner to
> just get_pid_task
> and put_task_struct().

I refactored this to use pidfd_get_pid and it seems like we can simply call
pid_task since we are already in an RCU read side critical section.

And to be pedantic, I added a WARN_ON_ONCE(!rcu_read_lock_held());
(although this is not required as lockdep should pretty much handle it
by default)

- KP

>
> >
> > > + if (!task || !task_storage_ptr(task)) {
> > "!task_storage_ptr(task)" is unnecessary, task_storage_lookup() should
> > have taken care of it.
> >
> >
> > > + err = -ENOENT;
> > > + goto out;
> > > + }
> > > +
> > > + sdata = task_storage_lookup(task, map, true);
> > > + put_pid(pid);
>
> [...]
>
> > > + .map_lookup_elem = bpf_pid_task_storage_lookup_elem,
> > > + .map_update_elem = bpf_pid_task_storage_update_elem,
> > > + .map_delete_elem = bpf_pid_task_storage_delete_elem,
> > Please exercise the syscall use cases also in the selftest.
>
> Will do. Thanks for the nudge :)

I also added another patch to exercise them for the other storage types too.

- KP

>
> >
> > > + .map_check_btf = bpf_local_storage_map_check_btf,
> > > + .map_btf_name = "bpf_local_storage_map",
> > > + .map_btf_id = _storage_map_btf_id,
> > > + .map_owner_storage_ptr = task_storage_ptr,
> > > +};
> > > +


Re: [PATCH bpf-next 1/5] bpf: Implement task local storage

2020-11-03 Thread KP Singh
[,,,]

> > + *
> > + * void *bpf_task_storage_get(struct bpf_map *map, void *task, void 
> > *value, u64 flags)
> After peeking patch 2,  I think the pointer type should be
> "struct task_struct *task" instead of "void *task".
>
> Same for bpf_task_storage_delete().

Done. Thanks!


[PATCH bpf-next v2 2/8] libbpf: Add support for task local storage

2020-11-03 Thread KP Singh
From: KP Singh 

Signed-off-by: KP Singh 
---
 tools/lib/bpf/libbpf_probes.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index 5482a9b7ae2d..bed00ca194f0 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 /* Copyright (c) 2019 Netronome Systems, Inc. */
 
+#include "linux/bpf.h"
 #include 
 #include 
 #include 
@@ -230,6 +231,7 @@ bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 
ifindex)
break;
case BPF_MAP_TYPE_SK_STORAGE:
case BPF_MAP_TYPE_INODE_STORAGE:
+   case BPF_MAP_TYPE_TASK_STORAGE:
btf_key_type_id = 1;
btf_value_type_id = 3;
value_size = 8;
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v2 8/8] bpf: Exercise syscall operations for inode and sk storage

2020-11-03 Thread KP Singh
From: KP Singh 

Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c  | 16 +++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
index feba23f8848b..2e64baabb50d 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -145,7 +145,7 @@ bool check_syscall_operations(int map_fd, int obj_fd)
 void test_test_local_storage(void)
 {
char tmp_exec_path[PATH_MAX] = "/tmp/copy_of_rmXX";
-   int err, serv_sk = -1, task_fd = -1;
+   int err, serv_sk = -1, task_fd = -1, rm_fd = -1;
struct local_storage *skel = NULL;
 
skel = local_storage__open_and_load();
@@ -169,6 +169,15 @@ void test_test_local_storage(void)
if (CHECK(err < 0, "copy_rm", "err %d errno %d\n", err, errno))
goto close_prog;
 
+   rm_fd = open(tmp_exec_path, O_RDONLY);
+   if (CHECK(rm_fd < 0, "open", "failed to open %s err:%d, errno:%d",
+ tmp_exec_path, rm_fd, errno))
+   goto close_prog;
+
+   if (!check_syscall_operations(bpf_map__fd(skel->maps.inode_storage_map),
+ rm_fd))
+   goto close_prog;
+
/* Sets skel->bss->monitored_pid to the pid of the forked child
 * forks a child process that executes tmp_exec_path and tries to
 * unlink its executable. This operation should be denied by the loaded
@@ -197,9 +206,14 @@ void test_test_local_storage(void)
CHECK(skel->data->sk_storage_result != 0, "sk_storage_result",
  "sk_local_storage not set\n");
 
+   if (!check_syscall_operations(bpf_map__fd(skel->maps.sk_storage_map),
+ serv_sk))
+   goto close_prog;
+
close(serv_sk);
 
 close_prog:
+   close(rm_fd);
close(task_fd);
local_storage__destroy(skel);
 }
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v2 4/8] bpf: Implement get_current_task_btf and RET_PTR_TO_BTF_ID

2020-11-03 Thread KP Singh
From: KP Singh 

The currently available bpf_get_current_task returns an unsigned integer
which can be used along with BPF_CORE_READ to read data from
the task_struct but still cannot be used as an input argument to a
helper that accepts an ARG_PTR_TO_BTF_ID of type task_struct.

In order to implement this helper a new return type, RET_PTR_TO_BTF_ID,
is added. This is similar to RET_PTR_TO_BTF_ID_OR_NULL but does not
require checking the nullness of returned pointer.

Signed-off-by: KP Singh 
---
 include/linux/bpf.h|  1 +
 include/uapi/linux/bpf.h   |  9 +
 kernel/bpf/verifier.c  |  7 +--
 kernel/trace/bpf_trace.c   | 16 
 tools/include/uapi/linux/bpf.h |  9 +
 5 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2fffd30e13ac..73d5381a5d5c 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -310,6 +310,7 @@ enum bpf_return_type {
RET_PTR_TO_BTF_ID_OR_NULL,  /* returns a pointer to a btf_id or 
NULL */
RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL, /* returns a pointer to a valid 
memory or a btf_id or NULL */
RET_PTR_TO_MEM_OR_BTF_ID,   /* returns a pointer to a valid memory 
or a btf_id */
+   RET_PTR_TO_BTF_ID,  /* returns a pointer to a btf_id */
 };
 
 /* eBPF function prototype used by verifier to allow BPF_CALLs from eBPF 
programs
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index f4037b2161a6..9879d6793e90 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -3779,6 +3779,14 @@ union bpf_attr {
  * 0 on success.
  *
  * **-ENOENT** if the bpf_local_storage cannot be found.
+ *
+ * struct task_struct *bpf_get_current_task_btf(void)
+ * Description
+ * Return a BTF pointer to the "current" task.
+ * This pointer can also be used in helpers that accept an
+ * *ARG_PTR_TO_BTF_ID* of type *task_struct*.
+ * Return
+ * Pointer to the current task.
  */
 #define __BPF_FUNC_MAPPER(FN)  \
FN(unspec), \
@@ -3939,6 +3947,7 @@ union bpf_attr {
FN(redirect_peer),  \
FN(task_storage_get),   \
FN(task_storage_delete),\
+   FN(get_current_task_btf),   \
/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index b0790876694f..314018e8fc12 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5186,11 +5186,14 @@ static int check_helper_call(struct bpf_verifier_env 
*env, int func_id, int insn
PTR_TO_BTF_ID : PTR_TO_BTF_ID_OR_NULL;
regs[BPF_REG_0].btf_id = meta.ret_btf_id;
}
-   } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL) {
+   } else if (fn->ret_type == RET_PTR_TO_BTF_ID_OR_NULL ||
+  fn->ret_type == RET_PTR_TO_BTF_ID) {
int ret_btf_id;
 
mark_reg_known_zero(env, regs, BPF_REG_0);
-   regs[BPF_REG_0].type = PTR_TO_BTF_ID_OR_NULL;
+   regs[BPF_REG_0].type = fn->ret_type == RET_PTR_TO_BTF_ID ?
+PTR_TO_BTF_ID :
+PTR_TO_BTF_ID_OR_NULL;
ret_btf_id = *fn->ret_btf_id;
if (ret_btf_id == 0) {
verbose(env, "invalid return type %d of func %s#%d\n",
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 4517c8b66518..e4515b0f62a8 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1022,6 +1022,20 @@ const struct bpf_func_proto bpf_get_current_task_proto = 
{
.ret_type   = RET_INTEGER,
 };
 
+BPF_CALL_0(bpf_get_current_task_btf)
+{
+   return (unsigned long) current;
+}
+
+BTF_ID_LIST_SINGLE(bpf_get_current_btf_ids, struct, task_struct)
+
+static const struct bpf_func_proto bpf_get_current_task_btf_proto = {
+   .func   = bpf_get_current_task_btf,
+   .gpl_only   = true,
+   .ret_type   = RET_PTR_TO_BTF_ID,
+   .ret_btf_id = _get_current_btf_ids[0],
+};
+
 BPF_CALL_2(bpf_current_task_under_cgroup, struct bpf_map *, map, u32, idx)
 {
struct bpf_array *array = container_of(map, struct bpf_array, map);
@@ -1265,6 +1279,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const 
struct bpf_prog *prog)
return _get_current_pid_tgid_proto;
case BPF_FUNC_get_current_task:
return _get_current_task_proto;
+   case BPF_FUNC_get_current_task_btf:
+   return _get_current_task_btf_proto;
case BPF_FUNC_get_current_uid_gid:
return _get_current_uid_gid_proto;
case BPF_FUNC_get_current_comm:
diff --git a/tools/include/ua

[PATCH bpf-next v2 1/8] bpf: Implement task local storage

2020-11-03 Thread KP Singh
From: KP Singh 

Similar to bpf_local_storage for sockets and inodes add local storage
for task_struct.

The life-cycle of storage is managed with the life-cycle of the
task_struct.  i.e. the storage is destroyed along with the owning task
with a callback to the bpf_task_storage_free from the task_free LSM
hook.

The BPF LSM allocates an __rcu pointer to the bpf_local_storage in
the security blob which are now stackable and can co-exist with other
LSMs.

The userspace map operations can be done by using a pid fd as a key
passed to the lookup, update and delete operations.

Signed-off-by: KP Singh 
---
 include/linux/bpf_lsm.h|  23 +++
 include/linux/bpf_types.h  |   1 +
 include/uapi/linux/bpf.h   |  39 
 kernel/bpf/Makefile|   1 +
 kernel/bpf/bpf_lsm.c   |   4 +
 kernel/bpf/bpf_task_storage.c  | 313 +
 kernel/bpf/syscall.c   |   3 +-
 kernel/bpf/verifier.c  |  10 ++
 security/bpf/hooks.c   |   2 +
 tools/include/uapi/linux/bpf.h |  39 
 10 files changed, 434 insertions(+), 1 deletion(-)
 create mode 100644 kernel/bpf/bpf_task_storage.c

diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index aaacb6aafc87..326cb68a3632 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -7,6 +7,7 @@
 #ifndef _LINUX_BPF_LSM_H
 #define _LINUX_BPF_LSM_H
 
+#include "linux/sched.h"
 #include 
 #include 
 
@@ -35,9 +36,21 @@ static inline struct bpf_storage_blob *bpf_inode(
return inode->i_security + bpf_lsm_blob_sizes.lbs_inode;
 }
 
+static inline struct bpf_storage_blob *bpf_task(
+   const struct task_struct *task)
+{
+   if (unlikely(!task->security))
+   return NULL;
+
+   return task->security + bpf_lsm_blob_sizes.lbs_task;
+}
+
 extern const struct bpf_func_proto bpf_inode_storage_get_proto;
 extern const struct bpf_func_proto bpf_inode_storage_delete_proto;
+extern const struct bpf_func_proto bpf_task_storage_get_proto;
+extern const struct bpf_func_proto bpf_task_storage_delete_proto;
 void bpf_inode_storage_free(struct inode *inode);
+void bpf_task_storage_free(struct task_struct *task);
 
 #else /* !CONFIG_BPF_LSM */
 
@@ -53,10 +66,20 @@ static inline struct bpf_storage_blob *bpf_inode(
return NULL;
 }
 
+static inline struct bpf_storage_blob *bpf_task(
+   const struct task_struct *task)
+{
+   return NULL;
+}
+
 static inline void bpf_inode_storage_free(struct inode *inode)
 {
 }
 
+static inline void bpf_task_storage_free(struct task_struct *task)
+{
+}
+
 #endif /* CONFIG_BPF_LSM */
 
 #endif /* _LINUX_BPF_LSM_H */
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 2e6f568377f1..99f7fd657d87 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -109,6 +109,7 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKHASH, sock_hash_ops)
 #endif
 #ifdef CONFIG_BPF_LSM
 BPF_MAP_TYPE(BPF_MAP_TYPE_INODE_STORAGE, inode_storage_map_ops)
+BPF_MAP_TYPE(BPF_MAP_TYPE_TASK_STORAGE, task_storage_map_ops)
 #endif
 BPF_MAP_TYPE(BPF_MAP_TYPE_CPUMAP, cpu_map_ops)
 #if defined(CONFIG_XDP_SOCKETS)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index e6ceac3f7d62..f4037b2161a6 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -157,6 +157,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_STRUCT_OPS,
BPF_MAP_TYPE_RINGBUF,
BPF_MAP_TYPE_INODE_STORAGE,
+   BPF_MAP_TYPE_TASK_STORAGE,
 };
 
 /* Note that tracing related programs such as
@@ -3742,6 +3743,42 @@ union bpf_attr {
  * Return
  * The helper returns **TC_ACT_REDIRECT** on success or
  * **TC_ACT_SHOT** on error.
+ *
+ * void *bpf_task_storage_get(struct bpf_map *map, struct task_struct *task, 
void *value, u64 flags)
+ * Description
+ * Get a bpf_local_storage from the *task*.
+ *
+ * Logically, it could be thought of as getting the value from
+ * a *map* with *task* as the **key**.  From this
+ * perspective,  the usage is not much different from
+ * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this
+ * helper enforces the key must be an task_struct and the map must 
also
+ * be a **BPF_MAP_TYPE_TASK_STORAGE**.
+ *
+ * Underneath, the value is stored locally at *task* instead of
+ * the *map*.  The *map* is used as the bpf-local-storage
+ * "type". The bpf-local-storage "type" (i.e. the *map*) is
+ * searched against all bpf_local_storage residing at *task*.
+ *
+ * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be
+ * used such that a new bpf_local_storage will be
+ * created if one does not exist.  *value* can be used
+ * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify
+ * the initial value of a bpf_local_storage.  If *

[PATCH bpf-next v2 3/8] bpftool: Add support for task local storage

2020-11-03 Thread KP Singh
From: KP Singh 

Signed-off-by: KP Singh 
---
 tools/bpf/bpftool/Documentation/bpftool-map.rst | 3 ++-
 tools/bpf/bpftool/bash-completion/bpftool   | 2 +-
 tools/bpf/bpftool/map.c | 4 +++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/tools/bpf/bpftool/Documentation/bpftool-map.rst 
b/tools/bpf/bpftool/Documentation/bpftool-map.rst
index dade10cdf295..3d52256ba75f 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-map.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-map.rst
@@ -50,7 +50,8 @@ MAP COMMANDS
 |  | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | 
**hash_of_maps**
 |  | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | 
**xskmap** | **sockhash**
 |  | **cgroup_storage** | **reuseport_sockarray** | 
**percpu_cgroup_storage**
-|  | **queue** | **stack** | **sk_storage** | **struct_ops** | 
**ringbuf** | **inode_storage** }
+|  | **queue** | **stack** | **sk_storage** | **struct_ops** | 
**ringbuf** | **inode_storage**
+   | **task_storage** }
 
 DESCRIPTION
 ===
diff --git a/tools/bpf/bpftool/bash-completion/bpftool 
b/tools/bpf/bpftool/bash-completion/bpftool
index 3f1da30c4da6..fdffbc64c65c 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -705,7 +705,7 @@ _bpftool()
 hash_of_maps devmap devmap_hash sockmap cpumap 
\
 xskmap sockhash cgroup_storage 
reuseport_sockarray \
 percpu_cgroup_storage queue stack sk_storage \
-struct_ops inode_storage' -- \
+struct_ops inode_storage task_storage' -- \
"$cur" ) )
 return 0
 ;;
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index a7efbd84fbcc..b400364ee054 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -51,6 +51,7 @@ const char * const map_type_name[] = {
[BPF_MAP_TYPE_STRUCT_OPS]   = "struct_ops",
[BPF_MAP_TYPE_RINGBUF]  = "ringbuf",
[BPF_MAP_TYPE_INODE_STORAGE]= "inode_storage",
+   [BPF_MAP_TYPE_TASK_STORAGE] = "task_storage",
 };
 
 const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
@@ -1464,7 +1465,8 @@ static int do_help(int argc, char **argv)
" lru_percpu_hash | lpm_trie | array_of_maps | 
hash_of_maps |\n"
" devmap | devmap_hash | sockmap | cpumap | 
xskmap | sockhash |\n"
" cgroup_storage | reuseport_sockarray | 
percpu_cgroup_storage |\n"
-   " queue | stack | sk_storage | struct_ops | 
ringbuf | inode_storage }\n"
+   " queue | stack | sk_storage | struct_ops | 
ringbuf | inode_storage |\n"
+   " task_storage }\n"
"   " HELP_SPEC_OPTIONS "\n"
"",
bin_name, argv[-2]);
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v2 5/8] bpf: Fix tests for local_storage

2020-11-03 Thread KP Singh
From: KP Singh 

The {inode,sk}_storage_result checking if the correct value was retrieved
was being clobbered unconditionally by the return value of the
bpf_{inode,sk}_storage_delete call.

Also, consistently use the newly added BPF_LOCAL_STORAGE_GET_F_CREATE
flag.

Fixes: cd324d7abb3d ("bpf: Add selftests for local_storage")
Signed-off-by: KP Singh 
---
 .../selftests/bpf/progs/local_storage.c   | 24 ---
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
index 0758ba229ae0..09529e33be98 100644
--- a/tools/testing/selftests/bpf/progs/local_storage.c
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -58,20 +58,22 @@ int BPF_PROG(unlink_hook, struct inode *dir, struct dentry 
*victim)
 {
__u32 pid = bpf_get_current_pid_tgid() >> 32;
struct dummy_storage *storage;
+   int err;
 
if (pid != monitored_pid)
return 0;
 
storage = bpf_inode_storage_get(_storage_map, victim->d_inode, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+   BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-   if (storage->value == DUMMY_STORAGE_VALUE)
+   if (storage->value != DUMMY_STORAGE_VALUE)
inode_storage_result = -1;
 
-   inode_storage_result =
-   bpf_inode_storage_delete(_storage_map, victim->d_inode);
+   err = bpf_inode_storage_delete(_storage_map, victim->d_inode);
+   if (!err)
+   inode_storage_result = err;
 
return 0;
 }
@@ -82,19 +84,23 @@ int BPF_PROG(socket_bind, struct socket *sock, struct 
sockaddr *address,
 {
__u32 pid = bpf_get_current_pid_tgid() >> 32;
struct dummy_storage *storage;
+   int err;
 
if (pid != monitored_pid)
return 0;
 
storage = bpf_sk_storage_get(_storage_map, sock->sk, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-   if (storage->value == DUMMY_STORAGE_VALUE)
+   if (storage->value != DUMMY_STORAGE_VALUE)
sk_storage_result = -1;
 
-   sk_storage_result = bpf_sk_storage_delete(_storage_map, sock->sk);
+   err = bpf_sk_storage_delete(_storage_map, sock->sk);
+   if (!err)
+   sk_storage_result = err;
+
return 0;
 }
 
@@ -109,7 +115,7 @@ int BPF_PROG(socket_post_create, struct socket *sock, int 
family, int type,
return 0;
 
storage = bpf_sk_storage_get(_storage_map, sock->sk, 0,
-BPF_SK_STORAGE_GET_F_CREATE);
+BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
@@ -131,7 +137,7 @@ int BPF_PROG(file_open, struct file *file)
return 0;
 
storage = bpf_inode_storage_get(_storage_map, file->f_inode, 0,
-BPF_LOCAL_STORAGE_GET_F_CREATE);
+   BPF_LOCAL_STORAGE_GET_F_CREATE);
if (!storage)
return 0;
 
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v2 0/8] Implement task_local_storage

2020-11-03 Thread KP Singh
From: KP Singh 

# v1 -> v2

- Updated the refcounting for task_struct and simplified conversion
  of fd -> struct pid.
- Some fixes suggested by Martin and Andrii, notably:
   * long return type for the bpf_task_storage_delete helper (update
 for bpf_inode_storage_delete will be sent separately).
   * Remove extra nullness check to task_storage_ptr in map syscall
 ops.
   * Changed the argument signature of the BPF helpers to use
 task_struct pointer in uapi headers.
   * Remove unnecessary verifier logic for the bpf_get_current_task_btf
 helper.
   * Split the changes for bpftool and libbpf.
- Exercised syscall operations for local storage (kept a simpler verison
  in test_local_storage.c, the eventual goal will be to update
  sk_storage_map.c for all local storage types).
- Formatting fixes + Rebase.

We already have socket and inode local storage since [1]

This patch series:

* Implements bpf_local_storage for task_struct.
* Implements the bpf_get_current_task_btf helper which returns a BTF
  pointer to the current task. Not only is this generally cleaner
  (reading from the task_struct currently requires BPF_CORE_READ), it
  also allows the BTF pointer to be used in task_local_storage helpers.
* In order to implement this helper, a RET_PTR_TO_BTF_ID is introduced
  which works similar to RET_PTR_TO_BTF_ID_OR_NULL but does not require
  a nullness check.
* Implements a detection in selftests which uses the
  task local storage to deny a running executable from unlinking itself.

[1]: 
https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=f836a56e84ffc9f1a1cd73f77e10404ca46a4616

KP Singh (8):
  bpf: Implement task local storage
  libbpf: Add support for task local storage
  bpftool: Add support for task local storage
  bpf: Implement get_current_task_btf and RET_PTR_TO_BTF_ID
  bpf: Fix tests for local_storage
  bpf: Update selftests for local_storage to use vmlinux.h
  bpf: Add tests for task_local_storage
  bpf: Exercise syscall operations for inode and sk storage

 include/linux/bpf.h   |   1 +
 include/linux/bpf_lsm.h   |  23 ++
 include/linux/bpf_types.h |   1 +
 include/uapi/linux/bpf.h  |  48 +++
 kernel/bpf/Makefile   |   1 +
 kernel/bpf/bpf_lsm.c  |   4 +
 kernel/bpf/bpf_task_storage.c | 313 ++
 kernel/bpf/syscall.c  |   3 +-
 kernel/bpf/verifier.c |  17 +-
 kernel/trace/bpf_trace.c  |  16 +
 security/bpf/hooks.c  |   2 +
 .../bpf/bpftool/Documentation/bpftool-map.rst |   3 +-
 tools/bpf/bpftool/bash-completion/bpftool |   2 +-
 tools/bpf/bpftool/map.c   |   4 +-
 tools/include/uapi/linux/bpf.h|  48 +++
 tools/lib/bpf/libbpf_probes.c |   2 +
 .../bpf/prog_tests/test_local_storage.c   | 181 +-
 .../selftests/bpf/progs/local_storage.c   |  87 +++--
 18 files changed, 705 insertions(+), 51 deletions(-)
 create mode 100644 kernel/bpf/bpf_task_storage.c

-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v2 6/8] bpf: Update selftests for local_storage to use vmlinux.h

2020-11-03 Thread KP Singh
From: KP Singh 

With the fixing of BTF pruning of embedded types being fixed, the test
can be simplified to use vmlinux.h

Signed-off-by: KP Singh 
---
 .../selftests/bpf/progs/local_storage.c   | 20 +--
 1 file changed, 1 insertion(+), 19 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/local_storage.c 
b/tools/testing/selftests/bpf/progs/local_storage.c
index 09529e33be98..ef3822bc7542 100644
--- a/tools/testing/selftests/bpf/progs/local_storage.c
+++ b/tools/testing/selftests/bpf/progs/local_storage.c
@@ -4,9 +4,8 @@
  * Copyright 2020 Google LLC.
  */
 
+#include "vmlinux.h"
 #include 
-#include 
-#include 
 #include 
 #include 
 
@@ -36,23 +35,6 @@ struct {
__type(value, struct dummy_storage);
 } sk_storage_map SEC(".maps");
 
-/* TODO Use vmlinux.h once BTF pruning for embedded types is fixed.
- */
-struct sock {} __attribute__((preserve_access_index));
-struct sockaddr {} __attribute__((preserve_access_index));
-struct socket {
-   struct sock *sk;
-} __attribute__((preserve_access_index));
-
-struct inode {} __attribute__((preserve_access_index));
-struct dentry {
-   struct inode *d_inode;
-} __attribute__((preserve_access_index));
-struct file {
-   struct inode *f_inode;
-} __attribute__((preserve_access_index));
-
-
 SEC("lsm/inode_unlink")
 int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
 {
-- 
2.29.1.341.ge80a0c044ae-goog



[PATCH bpf-next v2 7/8] bpf: Add tests for task_local_storage

2020-11-03 Thread KP Singh
From: KP Singh 

The test exercises the syscall based map operations by creating a pidfd
for the current process.

For verifying kernel / LSM functionality, the test implements a simple
MAC policy which denies an executable from unlinking itself. The LSM
program bprm_committed_creds sets a task_local_storage with a pointer to
the inode. This is then used to detect if the task is trying to unlink
itself in the inode_unlink LSM hook.

The test copies /bin/rm to /tmp and executes it in a child thread with
the intention of deleting itself. A successful test should prevent the
the running executable from deleting itself.

The temporary file is cleaned up later in the test.

Signed-off-by: KP Singh 
---
 .../bpf/prog_tests/test_local_storage.c   | 167 --
 .../selftests/bpf/progs/local_storage.c   |  45 -
 2 files changed, 194 insertions(+), 18 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c 
b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
index 91cd6f357246..feba23f8848b 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
@@ -4,30 +4,149 @@
  * Copyright (C) 2020 Google LLC.
  */
 
+#define _GNU_SOURCE
+
+#include 
+#include 
+#include 
 #include 
 #include 
 
 #include "local_storage.skel.h"
 #include "network_helpers.h"
 
-int create_and_unlink_file(void)
+static inline int sys_pidfd_open(pid_t pid, unsigned int flags)
+{
+   return syscall(__NR_pidfd_open, pid, flags);
+}
+
+unsigned int duration;
+
+#define TEST_STORAGE_VALUE 0xbeefdead
+
+struct storage {
+   void *inode;
+   unsigned int value;
+   /* Lock ensures that spin locked versions of local stoage operations
+* also work, most operations in this tests are still single threaded
+*/
+   struct bpf_spin_lock lock;
+};
+
+/* Copies an rm binary to a temp file. dest is a mkstemp template */
+int copy_rm(char *dest)
 {
-   char fname[PATH_MAX] = "/tmp/fileXX";
-   int fd;
+   int ret, fd_in, fd_out;
+   struct stat stat;
 
-   fd = mkstemp(fname);
-   if (fd < 0)
-   return fd;
+   fd_in = open("/bin/rm", O_RDONLY);
+   if (fd_in < 0)
+   return fd_in;
 
-   close(fd);
-   unlink(fname);
+   fd_out = mkstemp(dest);
+   if (fd_out < 0)
+   return fd_out;
+
+   ret = fstat(fd_in, );
+   if (ret == -1)
+   return errno;
+
+   ret = copy_file_range(fd_in, NULL, fd_out, NULL, stat.st_size, 0);
+   if (ret == -1)
+   return errno;
+
+   /* Set executable permission on the copied file */
+   ret = chmod(dest, 0100);
+   if (ret == -1)
+   return errno;
+
+   close(fd_in);
+   close(fd_out);
return 0;
 }
 
+/* Fork and exec the provided rm binary and return the exit code of the
+ * forked process and its pid.
+ */
+int run_self_unlink(int *monitored_pid, const char *rm_path)
+{
+   int child_pid, child_status, ret;
+   int null_fd;
+
+   child_pid = fork();
+   if (child_pid == 0) {
+   null_fd = open("/dev/null", O_WRONLY);
+   dup2(null_fd, STDOUT_FILENO);
+   dup2(null_fd, STDERR_FILENO);
+   close(null_fd);
+
+   *monitored_pid = getpid();
+   /* Use the copied /usr/bin/rm to delete itself
+* /tmp/copy_of_rm /tmp/copy_of_rm.
+*/
+   ret = execlp(rm_path, rm_path, rm_path, NULL);
+   if (ret)
+   exit(errno);
+   } else if (child_pid > 0) {
+   waitpid(child_pid, _status, 0);
+   return WEXITSTATUS(child_status);
+   }
+
+   return -EINVAL;
+}
+
+bool check_syscall_operations(int map_fd, int obj_fd)
+{
+   struct storage val = { .value = TEST_STORAGE_VALUE, .lock = { 0 } },
+  lookup_val = { .value = 0, .lock = { 0 } };
+   int err;
+
+   /* Looking up an existing element should fail initially */
+   err = bpf_map_lookup_elem_flags(map_fd, _fd, _val,
+   BPF_F_LOCK);
+   if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem",
+ "err:%d errno:%d\n", err, errno))
+   return false;
+
+   /* Create a new element */
+   err = bpf_map_update_elem(map_fd, _fd, ,
+ BPF_NOEXIST | BPF_F_LOCK);
+   if (CHECK(err < 0, "bpf_map_update_elem", "err:%d errno:%d\n", err,
+ errno))
+   return false;
+
+   /* Lookup the newly created element */
+   err = bpf_map_lookup_elem_flags(map_fd, _fd, _val,
+   BPF_F_LOCK);
+   if (CHECK(err < 0, "bpf_map_lookup_elem", "err:%

Re: [PATCH bpf-next v2 7/8] bpf: Add tests for task_local_storage

2020-11-03 Thread KP Singh
On Tue, Nov 3, 2020 at 7:47 PM Alexei Starovoitov
 wrote:
>
> On Tue, Nov 03, 2020 at 04:31:31PM +0100, KP Singh wrote:
> > +
> > +struct storage {
> > + void *inode;
> > + unsigned int value;
> > + /* Lock ensures that spin locked versions of local stoage operations
> > +  * also work, most operations in this tests are still single threaded
> > +  */
> > + struct bpf_spin_lock lock;
> > +};
>
> I think it's a good idea to test spin_lock in local_storage,
> but it seems the test is not doing it fully.
> It's only adding it to the storage, but the program is not accessing it.

I added it here just to check if the offset calculations (map->spin_lock_off)
are correctly happening for these new maps.

As mentioned in the updates, I do intend to generalize
tools/testing/selftests/bpf/map_tests/sk_storage_map.c which already has
 the threading logic to exercise bpf_spin_lock in storage maps.

Hope this is an okay plan?


Re: [PATCH bpf-next v2 2/8] libbpf: Add support for task local storage

2020-11-03 Thread KP Singh
On Tue, Nov 3, 2020 at 8:28 PM Andrii Nakryiko
 wrote:
>
> On Tue, Nov 3, 2020 at 7:34 AM KP Singh  wrote:
> >
> > From: KP Singh 
> >
> > Signed-off-by: KP Singh 
> > ---
> >  tools/lib/bpf/libbpf_probes.c | 2 ++
> >  1 file changed, 2 insertions(+)
> >
> > diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
> > index 5482a9b7ae2d..bed00ca194f0 100644
> > --- a/tools/lib/bpf/libbpf_probes.c
> > +++ b/tools/lib/bpf/libbpf_probes.c
> > @@ -1,6 +1,7 @@
> >  // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
> >  /* Copyright (c) 2019 Netronome Systems, Inc. */
> >
> > +#include "linux/bpf.h"
>
> why "", not <>?

I need to disable this vscode feature where it tries to be oversmart
and adds includes. Fixed.

- KP


Re: [PATCH bpf-next v2 8/8] bpf: Exercise syscall operations for inode and sk storage

2020-11-03 Thread KP Singh
On Tue, Nov 3, 2020 at 11:32 PM Song Liu  wrote:
>
>
>
> > On Nov 3, 2020, at 7:31 AM, KP Singh  wrote:
> >
> > From: KP Singh 
>
> A short commit log would be great...

Sure :) No excuses for not having one, will add it in the next revision.

- KP

[...]

> > +   serv_sk))
> > + goto close_prog;
>
> We shouldn't need this goto, otherwise we may leak serv_sk.

Good point, I will just move the close(serv_sk); along with the other
descriptor clean up.

>
> > +
> >   close(serv_sk);
> >
> > close_prog:
> > + close(rm_fd);
> >   close(task_fd);
> >   local_storage__destroy(skel);
> > }
> > --
> > 2.29.1.341.ge80a0c044ae-goog
> >
>


Re: [PATCH bpf-next v2 1/8] bpf: Implement task local storage

2020-11-03 Thread KP Singh
On Wed, Nov 4, 2020 at 12:47 AM Song Liu  wrote:
>
>
> > On Nov 3, 2020, at 7:31 AM, KP Singh  wrote:
> >
> > From: KP Singh 
> >
> > Similar to bpf_local_storage for sockets and inodes add local storage
> > for task_struct.
> >
> > The life-cycle of storage is managed with the life-cycle of the
> > task_struct.  i.e. the storage is destroyed along with the owning task
> > with a callback to the bpf_task_storage_free from the task_free LSM
> > hook.
> >
> > The BPF LSM allocates an __rcu pointer to the bpf_local_storage in
> > the security blob which are now stackable and can co-exist with other
> > LSMs.
> >
> > The userspace map operations can be done by using a pid fd as a key
> > passed to the lookup, update and delete operations.
> >
> > Signed-off-by: KP Singh 
>
> Acked-by: Song Liu 
>
> with a few nits:
>
> > ---
> > include/linux/bpf_lsm.h|  23 +++
> > include/linux/bpf_types.h  |   1 +
> > include/uapi/linux/bpf.h   |  39 
> > kernel/bpf/Makefile|   1 +
> > kernel/bpf/bpf_lsm.c   |   4 +
> > kernel/bpf/bpf_task_storage.c  | 313 +
> > kernel/bpf/syscall.c   |   3 +-
> > kernel/bpf/verifier.c  |  10 ++
> > security/bpf/hooks.c   |   2 +
> > tools/include/uapi/linux/bpf.h |  39 
> > 10 files changed, 434 insertions(+), 1 deletion(-)
> > create mode 100644 kernel/bpf/bpf_task_storage.c
> >
> > diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
> > index aaacb6aafc87..326cb68a3632 100644
> > --- a/include/linux/bpf_lsm.h
> > +++ b/include/linux/bpf_lsm.h
> > @@ -7,6 +7,7 @@
> > #ifndef _LINUX_BPF_LSM_H
> > #define _LINUX_BPF_LSM_H
> >
> > +#include "linux/sched.h"
>
> vscode?

Yep, turns out it was clangd (which I use in vscode) and I needed to
pass "--header-insertion=never"
when starting the clangd server, I fixed all other instances as well.

- KP


Re: [PATCH bpf-next v2 7/8] bpf: Add tests for task_local_storage

2020-11-03 Thread KP Singh
On Tue, Nov 3, 2020 at 7:59 PM KP Singh  wrote:
>
> On Tue, Nov 3, 2020 at 7:47 PM Alexei Starovoitov
>  wrote:
> >
> > On Tue, Nov 03, 2020 at 04:31:31PM +0100, KP Singh wrote:
> > > +
> > > +struct storage {
> > > + void *inode;
> > > + unsigned int value;
> > > + /* Lock ensures that spin locked versions of local stoage operations
> > > +  * also work, most operations in this tests are still single 
> > > threaded
> > > +  */
> > > + struct bpf_spin_lock lock;
> > > +};
> >
> > I think it's a good idea to test spin_lock in local_storage,
> > but it seems the test is not doing it fully.
> > It's only adding it to the storage, but the program is not accessing it.
>
> I added it here just to check if the offset calculations (map->spin_lock_off)
> are correctly happening for these new maps.
>
> As mentioned in the updates, I do intend to generalize
> tools/testing/selftests/bpf/map_tests/sk_storage_map.c which already has
>  the threading logic to exercise bpf_spin_lock in storage maps.
>

Actually, after I added simple bpf_spin_{lock, unlock} to the test programs, I
ended up realizing that we have not exposed spin locks to LSM programs
for now, this is because they inherit the tracing helpers.

I saw the docs mention that these are not exposed to tracing programs due to
insufficient preemption checks. Do you think it would be okay to allow them
for LSM programs?


- KP

> Hope this is an okay plan?


Re: [PATCH bpf-next v2 7/8] bpf: Add tests for task_local_storage

2020-11-03 Thread KP Singh
[...]

> >
> > I saw the docs mention that these are not exposed to tracing programs due to
> > insufficient preemption checks. Do you think it would be okay to allow them
> > for LSM programs?
>
> hmm. Isn't it allowed already?
> The verifier does:
> if ((is_tracing_prog_type(prog_type) ||
>  prog_type == BPF_PROG_TYPE_SOCKET_FILTER) &&
> map_value_has_spin_lock(map)) {
> verbose(env, "tracing progs cannot use bpf_spin_lock yet\n");
> return -EINVAL;
> }
>
> BPF_PROG_TYPE_LSM is not in this list.

The verifier does not have any problem, it's just that the helpers are not
exposed to LSM programs via bpf_lsm_func_proto.

So all we need is:

diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 61f8cc52fd5b..93383df2140b 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -63,6 +63,10 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const
struct bpf_prog *prog)
return _task_storage_get_proto;
case BPF_FUNC_task_storage_delete:
return _task_storage_delete_proto;
+   case BPF_FUNC_spin_lock:
+   return _spin_lock_proto;
+   case BPF_FUNC_spin_unlock:
+   return _spin_unlock_proto;
default:
return tracing_prog_func_proto(func_id, prog);
}


Re: [PATCH v3 bpf-next 1/4] bpf: enable task local storage for tracing programs

2021-01-31 Thread KP Singh
On Thu, Jan 28, 2021 at 1:20 AM Song Liu  wrote:
>
> To access per-task data, BPF programs usually creates a hash table with
> pid as the key. This is not ideal because:
>  1. The user need to estimate the proper size of the hash table, which may
> be inaccurate;
>  2. Big hash tables are slow;
>  3. To clean up the data properly during task terminations, the user need
> to write extra logic.
>
> Task local storage overcomes these issues and offers a better option for
> these per-task data. Task local storage is only available to BPF_LSM. Now
> enable it for tracing programs.
>
> Unlike LSM progreams, tracing programs can be called in IRQ contexts.

nit: typo *programs

> Helpers that accesses task local storage are updated to use

nit: Helpers that access..

> raw_spin_lock_irqsave() instead of raw_spin_lock_bh().
>
> Tracing programs can attach to functions on the task free path, e.g.
> exit_creds(). To avoid allocating task local storage after
> bpf_task_storage_free(). bpf_task_storage_get() is updated to not allocate
> new storage when the task is not refcounted (task->usage == 0).
>
> Signed-off-by: Song Liu 

Acked-by: KP Singh 

Thanks for adding better commit descriptions :)

I think checking the usage before adding storage should work for the
task exit path (I could not think of cases where it would break).
Would also be nice to check with Martin and Hao about this.


Re: [RFC v1 12/14] krsi: Add an eBPF helper function to get the value of an env variable

2019-09-17 Thread KP Singh
On 17-Sep 16:58, Yonghong Song wrote:
> 
> 
> On 9/16/19 6:00 AM, KP Singh wrote:
> > Thanks for reviewing!
> > 
> > On 15-Sep 00:16, Yonghong Song wrote:
> >>
> >>
> >> On 9/10/19 12:55 PM, KP Singh wrote:
> >>> From: KP Singh 
> >>
> >> This patch cannot apply cleanly.
> >>
> >> -bash-4.4$ git apply ~/p12.txt
> >> error: patch failed: include/uapi/linux/bpf.h:2715
> >> error: include/uapi/linux/bpf.h: patch does not apply
> >> error: patch failed: tools/include/uapi/linux/bpf.h:2715
> >> error: tools/include/uapi/linux/bpf.h: patch does not apply
> >> -bash-4.4$
> > 
> > I am not sure why this is happening, I tried:
> > 
> > git clone \
> >https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git && \
> >cd linux-next && \
> >git checkout -b review v5.3-rc6 && \
> >wget -P /tmp https://lore.kernel.org/patchwork/series/410101/mbox && \
> >git am /tmp/mbox
> > 
> > and it worked.
> > 
> > This seems to work too:
> > 
> >patch -p1 < .patch
> > 
> > Can you try with "git am" please?
> 
> Will try next time when reviewing the tree.

Thanks!

> 
> > 
> >>
> >>>
> >>> The helper returns the value of the environment variable in the buffer
> >>> that is passed to it. If the var is set multiple times, the helper
> >>> returns all the values as null separated strings.
> >>>
> >>> If the buffer is too short for these values, the helper tries to fill it
> >>> the best it can and guarantees that the value returned in the buffer
> >>> is always null terminated. After the buffer is filled, the helper keeps
> >>> counting the number of times the environment variable is set in the
> >>> envp.
> >>>
> >>> The return value of the helper is an u64 value which carries two pieces
> >>> of information.
> >>>
> >>> * The upper 32 bits are a u32 value signifying the number of times
> >>>   the environment variable is set in the envp.
> >>
> >> Not sure how useful this 'upper 32' bit value is. What user expected to do?
> >>
> >> Another option is to have upper 32 bits encode the required buffer size
> >> to hold all values. This may cause some kind of user space action, e.g.,
> >> to replace the program with new program with larger per cpu map value size?
> >>
> > 
> > The upper 32-bit value is actually an important part of the LSM's MAC
> > policy. It allows the user to:
> > 
> > - Return an -EPERM when if the environment variable is set more than
> >once.
> > - Log a warning (this is what we are doing in the example) so
> >this is flagged as a potential malicious actor.
> 
> So the intention is to catch cases where the env variable
> to set by more than once, not exactly to capture all the values
> of the env variable.
> 
> Then may be there is no need to record the values once the number of 
> values is more than one? Do you have use case for what user to react if
> there are more than one setting?

Since KRSI intends to be an API for creating MAC and Audit Policies,
it would be best to leave the decision to the user, as to what they 
intend to do with the extra information. In general, having both the 
values helps in building more context about the potential malicious actor.

What should ideally be done is returning an -EPERM error, because there
should be no reason to set an env variable twice. 
In fact, two+ env vars can only be passed directly to the 
execve system call arguments. Shells (e.g. bash) remove the duplicate 
value before passing the envv to the system call.

> 
> > 
> >>> * The lower 32 bits are a s32 value signifying the number of bytes
> >>>   written to the buffer or an error code. >
> >>> Since the value of the environment variable can be very long and exceed
> >>> what can be allocated on the BPF stack, a per-cpu array can be used
> >>> instead:
> >>>
> >>> struct bpf_map_def SEC("maps") env_map = {
> >>>   .type = BPF_MAP_TYPE_PERCPU_ARRAY,
> >>>   .key_size = sizeof(u32),
> >>>   .value_size = 4096,
> >>>   .max_entries = 1,
> >>> };
> >>
> >> Could you use use map definition with SEC(".maps")?
> > 
> > Sure, I added this example program in the commit message. Will upd

[RFC v1 00/14] Kernel Runtime Security Instrumentation

2019-09-10 Thread KP Singh
From: KP Singh 

# Motivation

Signaling and mitigation are two key aspects of security which go
hand-in-hand.  Signals provide the necessary context to narrow down a
malicious actor and are key to creating effective MAC policies to
mitigate/prevent the malicious actor.

One can obtain signals from the kernel by using the audit infrastructure.
System-wide MAC is done separately as a part of the LSMs (eg. SELinux,
AppArmor).

Augmenting the signal information provided by audit requires kernel
changes to audit, its policy language and user-space components.
Furthermore, building a MAC policy based on the newly added signal
requires changes to various LSMs and their own respective policy
languages.

KRSI attempts to solve this problem by providing a common policy API in
the form of security focussed eBPF helpers and a common surface for
creating dynamic (not requiring re-compilation of the kernel) MAC and
Audit policies by attaching eBPF programs to the various LSM hooks.


# Why an LSM?

Linux Security Modules target security behaviours rather than the API. For
example, it's easy to miss out a newly added system call for executing
processes (eg. execve, execveat etc.) while the LSM framework ensures that
all process executions trigger the relevant hooks irrespective of how the
process was executed.

Allowing users to attach eBPF programs to LSM hooks also benefits the LSM
eco-system by enabling feedback from the security community about the kind
of behaviours that the LSM should be targeting.


# How does it work?

NOTE: The cover letter will be assimilated into the documentation
(Documentation/security/) as a part of the patch series.

## SecurityFS Interface

KRSI hooks create a file in securityfs to which eBPF programs can be
attached.  This file maps to a single LSM hook but adds a layer of
indirection thus shielding the user-space from any changes to the
underlying hook. This includes changes like renaming of the underlying
hook or replacing the hook with another that maps better to the security
behaviour represented.

For Example:

/sys/kernel/security/krsi/process_execution -> bprm_check_security

## eBPF Program / Helpers

In order to keep things simple for the user, KRSI intends to provide one
eBPF program type. Since, there is only one type of context for a given
eBPF program type, the members of the KRSI context are selectively
populated depending on the hook.

KRSI is conservative about the access into the context and expects users
to use the helpers as an API for getting the required information. This
helps limit the internals of the LSM exposed to the user and maintain
backward compatibility. It also allows the maintainers to update the
structure of the context without worrying about breaking user-space.

The intention is to keep the helpers precise and not depend on kernel
headers or internals strengthening the backward compatibility and
usability across a large and diverse fleet.

Precise helpers also help in tackling seemingly intractable problems. For
example, a helper to dump all environment variables is hard because the
environment variables can be 32 pages long. While a helper to only extract
certain relevant environment variables (e.g. LD_PRELOAD, HISTFILESIZE)
helps in building a more reliable and precise signal with lower overhead.

## Attachment

A privileged (CAP_SYS_ADMIN for loading / attaching eBPF programs)
user-space program opens the securityfs file and the eBPF program
(BPF_PROG_LOAD) and attaches (BPF_PROG_ATTACH) the eBPF program to the
hook.

hook_fd = open("/sys/kernel/security/krsi/process_execution", O_RDWR) 
prog_fd = bpf(BPF_PROG_LOAD, ...) 
bpf(BPF_PROG_ATTACH, hook_fd, prog_fd)

There can be more than one program attached to a hook and attaching a
program with the same name replaces the existing program. The user can see
the programs attached to the hook as follows:

cat /sys/kernel/security/krsi/process_execution 
env_dumper
my_auditor

## Audit / MAC Policy

The userspace controls the policy by installing eBPF programs to the LSM
hook.  If any of the attached eBPF programs return an error (-ENOPERM),
the action represented by the hook is denied.

The audit logs are written using a format chosen by the eBPF program to
the perf events buffer (using bpf_perf_event_output) and can be further
processed in user-space.


# Current Status

The RFC provides a proto-type implementation with a few helpers and a
sample program samples/bpf/krsi_kern.c that hooks into process execution
and logs specific environment variables to the perf events buffer.

The user-space program samples/bpf/krsi_user.c loads the eBPF program,
configures the environment variable that needs to be audited and listens
for perf events.


KP Singh (14):
  krsi: Add a skeleton and config options for the KRSI LSM
  krsi: Introduce types for KRSI eBPF
  bpf: krsi: sync BPF UAPI header with tools
  krsi: Add support in libbpf for BPF_P

[RFC v1 01/14] krsi: Add a skeleton and config options for the KRSI LSM

2019-09-10 Thread KP Singh
From: KP Singh 

The LSM can be enabled by:

- Enabling CONFIG_SECURITY_KRSI.
- Adding "krsi" to the CONFIG_LSM string.

Signed-off-by: KP Singh 
---
 MAINTAINERS|  5 +
 security/Kconfig   |  1 +
 security/Makefile  |  2 ++
 security/krsi/Kconfig  | 22 ++
 security/krsi/Makefile |  1 +
 security/krsi/krsi.c   | 24 
 6 files changed, 55 insertions(+)
 create mode 100644 security/krsi/Kconfig
 create mode 100644 security/krsi/Makefile
 create mode 100644 security/krsi/krsi.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 9cbcf167bdd0..8e0364391d8b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9002,6 +9002,11 @@ F:   include/linux/kprobes.h
 F: include/asm-generic/kprobes.h
 F: kernel/kprobes.c
 
+KRSI SECURITY MODULE
+M:     KP Singh 
+S: Supported
+F: security/krsi/
+
 KS0108 LCD CONTROLLER DRIVER
 M: Miguel Ojeda Sandonis 
 S: Maintained
diff --git a/security/Kconfig b/security/Kconfig
index 0d65594b5196..febf7953803f 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -236,6 +236,7 @@ source "security/tomoyo/Kconfig"
 source "security/apparmor/Kconfig"
 source "security/loadpin/Kconfig"
 source "security/yama/Kconfig"
+source "security/krsi/Kconfig"
 source "security/safesetid/Kconfig"
 
 source "security/integrity/Kconfig"
diff --git a/security/Makefile b/security/Makefile
index c598b904938f..25779ce89bf2 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -9,6 +9,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack
 subdir-$(CONFIG_SECURITY_TOMOYO)+= tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
 subdir-$(CONFIG_SECURITY_YAMA) += yama
+subdir-$(CONFIG_SECURITY_KRSI) += krsi
 subdir-$(CONFIG_SECURITY_LOADPIN)  += loadpin
 subdir-$(CONFIG_SECURITY_SAFESETID)+= safesetid
 
@@ -25,6 +26,7 @@ obj-$(CONFIG_AUDIT)   += lsm_audit.o
 obj-$(CONFIG_SECURITY_TOMOYO)  += tomoyo/
 obj-$(CONFIG_SECURITY_APPARMOR)+= apparmor/
 obj-$(CONFIG_SECURITY_YAMA)+= yama/
+obj-$(CONFIG_SECURITY_KRSI)+= krsi/
 obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
 obj-$(CONFIG_SECURITY_SAFESETID)   += safesetid/
 obj-$(CONFIG_CGROUP_DEVICE)+= device_cgroup.o
diff --git a/security/krsi/Kconfig b/security/krsi/Kconfig
new file mode 100644
index ..bf5eab4355af
--- /dev/null
+++ b/security/krsi/Kconfig
@@ -0,0 +1,22 @@
+config SECURITY_KRSI
+   bool "Runtime Security Instrumentation (BPF-based MAC and audit policy)"
+   depends on SECURITY
+   depends on SECURITYFS
+   depends on BPF
+   depends on BPF_SYSCALL
+   help
+ This selects the Kernel Runtime Security Instrumentation
+ LSM which allows dynamic instrumentation of the security hooks with
+ eBPF programs. The LSM creates per-hook files in securityfs to which
+ eBPF programs can be attached.
+
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_KRSI_ENFORCE
+   bool "Deny operations based on the evaluation of the attached programs"
+   depends on SECURITY_KRSI
+   help
+ eBPF programs attached to hooks can be used for both auditing and
+ enforcement. Enabling enforcement implies that the evaluation result
+ from the attached eBPF programs will allow and deny the operation
+ guarded by the security hook.
diff --git a/security/krsi/Makefile b/security/krsi/Makefile
new file mode 100644
index ..73320e8d16f8
--- /dev/null
+++ b/security/krsi/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SECURITY_KRSI) := krsi.o
diff --git a/security/krsi/krsi.c b/security/krsi/krsi.c
new file mode 100644
index ..9ce4f56fb78d
--- /dev/null
+++ b/security/krsi/krsi.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include 
+
+static int krsi_process_execution(struct linux_binprm *bprm)
+{
+   return 0;
+}
+
+static struct security_hook_list krsi_hooks[] __lsm_ro_after_init = {
+   LSM_HOOK_INIT(bprm_check_security, krsi_process_execution),
+};
+
+static int __init krsi_init(void)
+{
+   security_add_hooks(krsi_hooks, ARRAY_SIZE(krsi_hooks), "krsi");
+   pr_info("eBPF and LSM are friends now.\n");
+   return 0;
+}
+
+DEFINE_LSM(krsi) = {
+   .name = "krsi",
+   .init = krsi_init,
+};
-- 
2.20.1



[RFC v1 05/14] krsi: Initialize KRSI hooks and create files in securityfs

2019-09-10 Thread KP Singh
From: KP Singh 

The LSM creates files in securityfs for each hook registered with the
LSM.

/sys/kernel/security/bpf/

The initialization of the hooks is done collectively in an internal
header "hooks.h" which results in:

* Creation of a file for the hook in the securityfs.
* Allocation of a krsi_hook data structure which stores a pointer to the
  dentry of the newly created file in securityfs.
* A pointer to the krsi_hook data structure is stored in the private
  d_fsdata of dentry of the file created in securityFS.

These files will later be used to specify an attachment target during
BPF_PROG_LOAD.

Signed-off-by: KP Singh 
---
 security/krsi/Makefile|  4 +-
 security/krsi/include/hooks.h | 21 
 security/krsi/include/krsi_fs.h   | 19 +++
 security/krsi/include/krsi_init.h | 45 
 security/krsi/krsi.c  | 16 +-
 security/krsi/krsi_fs.c   | 88 +++
 6 files changed, 191 insertions(+), 2 deletions(-)
 create mode 100644 security/krsi/include/hooks.h
 create mode 100644 security/krsi/include/krsi_fs.h
 create mode 100644 security/krsi/include/krsi_init.h
 create mode 100644 security/krsi/krsi_fs.c

diff --git a/security/krsi/Makefile b/security/krsi/Makefile
index 660cc1f422fd..4586241f16e1 100644
--- a/security/krsi/Makefile
+++ b/security/krsi/Makefile
@@ -1 +1,3 @@
-obj-$(CONFIG_SECURITY_KRSI) := krsi.o ops.o
+obj-$(CONFIG_SECURITY_KRSI) := krsi.o krsi_fs.o ops.o
+
+ccflags-y := -I$(srctree)/security/krsi -I$(srctree)/security/krsi/include
diff --git a/security/krsi/include/hooks.h b/security/krsi/include/hooks.h
new file mode 100644
index ..e070c452b5de
--- /dev/null
+++ b/security/krsi/include/hooks.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * The hooks for the KRSI LSM are declared in this file.
+ *
+ * This header MUST NOT be included directly and should
+ * be only used to initialize the hooks lists.
+ *
+ * Format:
+ *
+ *   KRSI_HOOK_INIT(TYPE, NAME, LSM_HOOK, KRSI_HOOK_FN)
+ *
+ * KRSI adds one layer of indirection between the name of the hook and the name
+ * it exposes to the userspace in Security FS to prevent the userspace from
+ * breaking in case the name of the hook changes in the kernel or if there's
+ * another LSM hook that maps better to the represented security behaviour.
+ */
+KRSI_HOOK_INIT(PROCESS_EXECUTION,
+  process_execution,
+  bprm_check_security,
+  krsi_process_execution)
diff --git a/security/krsi/include/krsi_fs.h b/security/krsi/include/krsi_fs.h
new file mode 100644
index ..38134661d8d6
--- /dev/null
+++ b/security/krsi/include/krsi_fs.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _KRSI_FS_H
+#define _KRSI_FS_H
+
+#include 
+#include 
+#include 
+
+bool is_krsi_hook_file(struct file *f);
+
+/*
+ * The name of the directory created in securityfs
+ *
+ * /sys/kernel/security/
+ */
+#define KRSI_SFS_NAME "krsi"
+
+#endif /* _KRSI_FS_H */
diff --git a/security/krsi/include/krsi_init.h 
b/security/krsi/include/krsi_init.h
new file mode 100644
index ..68755182a031
--- /dev/null
+++ b/security/krsi/include/krsi_init.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _KRSI_INIT_H
+#define _KRSI_INIT_H
+
+#include "krsi_fs.h"
+
+enum krsi_hook_type {
+   PROCESS_EXECUTION,
+   __MAX_KRSI_HOOK_TYPE, /* delimiter */
+};
+
+extern int krsi_fs_initialized;
+/*
+ * The LSM creates one file per hook.
+ *
+ * A pointer to krsi_hook data structure is stored in the
+ * private fsdata of the dentry of the per-hook file created
+ * in securityfs.
+ */
+struct krsi_hook {
+   /*
+* The name of the security hook, a file with this name will be created
+* in the securityfs.
+*/
+   const char *name;
+   /*
+* The type of the LSM hook, the LSM uses this to index the list of the
+* hooks to run the eBPF programs that may have been attached.
+*/
+   enum krsi_hook_type h_type;
+   /*
+* The dentry of the file created in securityfs.
+*/
+   struct dentry *h_dentry;
+};
+
+extern struct krsi_hook krsi_hooks_list[];
+
+#define krsi_for_each_hook(hook) \
+   for ((hook) = _hooks_list[0]; \
+(hook) < _hooks_list[__MAX_KRSI_HOOK_TYPE]; \
+(hook)++)
+
+#endif /* _KRSI_INIT_H */
diff --git a/security/krsi/krsi.c b/security/krsi/krsi.c
index 9ce4f56fb78d..77d7e2f91172 100644
--- a/security/krsi/krsi.c
+++ b/security/krsi/krsi.c
@@ -2,13 +2,27 @@
 
 #include 
 
+#include "krsi_init.h"
+
+struct krsi_hook krsi_hooks_list[] = {
+   #define KRSI_HOOK_INIT(TYPE, NAME, H, I) \
+   [TYPE] = { \
+   .h_type = TYPE, \
+   .name = #NAME, \
+   },
+   #include "hooks.h"
+   #undef KRSI_HOOK_INIT
+};
+
 static int krsi_process_exe

[RFC v1 07/14] krsi: Check for premissions on eBPF attachment

2019-09-10 Thread KP Singh
From: KP Singh 

Add validation checks for the attachment of eBPF programs.

The following permissions are required:

- CAP_SYS_ADMIN to load eBPF programs
- CAP_MAC_ADMIN (to update the policy of an LSM)
- The securityfs file being a KRSI hook and writable (O_RDWR)

Signed-off-by: KP Singh 
---
 security/krsi/ops.c | 20 
 1 file changed, 20 insertions(+)

diff --git a/security/krsi/ops.c b/security/krsi/ops.c
index cf4d06189aa1..a61508b7018f 100644
--- a/security/krsi/ops.c
+++ b/security/krsi/ops.c
@@ -23,11 +23,31 @@ static struct krsi_hook *get_hook_from_fd(int fd)
goto error;
}
 
+   /*
+* Only CAP_MAC_ADMIN users are allowed to make
+* changes to LSM hooks
+*/
+   if (!capable(CAP_MAC_ADMIN)) {
+   ret = -EPERM;
+   goto error;
+   }
+
if (!is_krsi_hook_file(f.file)) {
ret = -EINVAL;
goto error;
}
 
+   /*
+* It's wrong to attach the program to the hook
+* if the file is not opened for a write. Note that,
+* this is an EBADF and not an EPERM because the file
+* has been opened with an incorrect mode.
+*/
+   if (!(f.file->f_mode & FMODE_WRITE)) {
+   ret = -EBADF;
+   goto error;
+   }
+
/*
 * The securityfs dentry never disappears, so we don't need to take a
 * reference to it.
-- 
2.20.1



[RFC v1 10/14] krsi: Handle attachment of the same program

2019-09-10 Thread KP Singh
From: KP Singh 

Allow the userspace to attach a newer version of a program without
having duplicates of the same program.

If BPF_F_ALLOW_OVERRIDE is passed, the attachment logic compares the
name of the new program to the names of existing attached programs. The
names are only compared till a "__" (or '\0', if there is no "__"). If
a successful match is found, the existing program is replaced with the
newer attachment.

./krsi Attaches "env_dumper__v1" followed by "env_dumper__v2"
to the process_execution hook of the KRSI LSM.

./krsi
./krsi

Before:

  cat /sys/kernel/security/krsi/process_execution
  env_dumper__v1
  env_dumper__v2

After:

  cat /sys/kernel/security/krsi/process_execution
  env_dumper__v2

Signed-off-by: KP Singh 
---
 security/krsi/ops.c | 53 -
 1 file changed, 52 insertions(+), 1 deletion(-)

diff --git a/security/krsi/ops.c b/security/krsi/ops.c
index 57bd304a03f4..1f4df920139c 100644
--- a/security/krsi/ops.c
+++ b/security/krsi/ops.c
@@ -65,11 +65,52 @@ static struct krsi_hook *get_hook_from_fd(int fd)
return ERR_PTR(ret);
 }
 
+/*
+ * match_prog_name matches the name of the program till "__"
+ * or the end of the string is encountered. This allows
+ * a different version of the same program to be loaded.
+ *
+ * For example:
+ *
+ * env_dumper__v1 is matched with env_dumper__v2
+ *
+ */
+static bool match_prog_name(char *a, char *b)
+{
+   int m, n;
+   char *end;
+
+   end = strstr(a, "__");
+   n = end ? end - a : strlen(a);
+
+   end = strstr(b, "__");
+   m = end ? end - b : strlen(b);
+
+   if (m != n)
+   return false;
+
+   return strncmp(a, b, n) == 0;
+}
+
+static struct bpf_prog *find_attached_prog(struct bpf_prog_array *array,
+  struct bpf_prog *prog)
+{
+   struct bpf_prog_array_item *item = array->items;
+
+   for (; item->prog; item++) {
+   if (match_prog_name(item->prog->aux->name, prog->aux->name))
+   return item->prog;
+   }
+
+   return NULL;
+}
+
 int krsi_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog)
 {
struct bpf_prog_array *old_array;
struct bpf_prog_array *new_array;
struct krsi_hook *h;
+   struct bpf_prog *old_prog;
int ret = 0;
 
h = get_hook_from_fd(attr->target_fd);
@@ -79,8 +120,18 @@ int krsi_prog_attach(const union bpf_attr *attr, struct 
bpf_prog *prog)
mutex_lock(>mutex);
old_array = rcu_dereference_protected(h->progs,
  lockdep_is_held(>mutex));
+   /*
+* Check if a matching program with already exists and replace
+* the existing program will be overridden if BPF_F_ALLOW_OVERRIDE
+* is specified in the attach flags.
+*/
+   old_prog = find_attached_prog(old_array, prog);
+   if (old_prog && !(attr->attach_flags & BPF_F_ALLOW_OVERRIDE)) {
+   ret = -EEXIST;
+   goto unlock;
+   }
 
-   ret = bpf_prog_array_copy(old_array, NULL, prog, _array);
+   ret = bpf_prog_array_copy(old_array, old_prog, prog, _array);
if (ret < 0) {
ret = -ENOMEM;
goto unlock;
-- 
2.20.1



[RFC v1 11/14] krsi: Pin argument pages in bprm_check_security hook

2019-09-10 Thread KP Singh
From: KP Singh 

Pin the memory allocated to the the argv + envv for the new
process and passes it in the context to the eBPF programs attached to
the hook.

The get_user_pages_remote cannot be called from an eBPF helper because
the helpers run in atomic context and the get_user_pages_remote function
can sleep.

The following heuristics can be added as an optimization:

- Don't pin the pages if no eBPF programs are attached.
- Don't pin the pages if none of the eBPF programs depend on the
  information. This would require introspection of the byte-code and
  checking if certain helpers are called.

Signed-off-by: KP Singh 
---
 security/krsi/include/krsi_init.h |  3 ++
 security/krsi/krsi.c  | 56 +++
 2 files changed, 59 insertions(+)

diff --git a/security/krsi/include/krsi_init.h 
b/security/krsi/include/krsi_init.h
index 4e17ecacd4ed..6152847c3b08 100644
--- a/security/krsi/include/krsi_init.h
+++ b/security/krsi/include/krsi_init.h
@@ -16,6 +16,9 @@ extern int krsi_fs_initialized;
 
 struct krsi_bprm_ctx {
struct linux_binprm *bprm;
+   char *arg_pages;
+   unsigned long num_arg_pages;
+   unsigned long max_arg_offset;
 };
 
 /*
diff --git a/security/krsi/krsi.c b/security/krsi/krsi.c
index d3a4a361c192..00a7150c1b22 100644
--- a/security/krsi/krsi.c
+++ b/security/krsi/krsi.c
@@ -4,6 +4,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include "krsi_init.h"
 
@@ -17,6 +19,53 @@ struct krsi_hook krsi_hooks_list[] = {
#undef KRSI_HOOK_INIT
 };
 
+static int pin_arg_pages(struct krsi_bprm_ctx *ctx)
+{
+   int ret = 0;
+   char *kaddr;
+   struct page *page;
+   unsigned long i, pos, num_arg_pages;
+   struct linux_binprm *bprm = ctx->bprm;
+   char *buf;
+
+   /*
+* The bprm->vma_pages does not have the correct count
+* for execution that is done by a kernel thread using the UMH.
+* vm_pages is updated in acct_arg_size and bails
+* out if current->mm is NULL (which is the case for a kernel thread).
+* It's safer to use vma_pages(struct linux_binprm*) to get the
+* actual number
+*/
+   num_arg_pages = vma_pages(bprm->vma);
+   if (!num_arg_pages)
+   return -ENOMEM;
+
+   buf = kmalloc_array(num_arg_pages, PAGE_SIZE, GFP_KERNEL);
+   if (!buf)
+   return -ENOMEM;
+
+   for (i = 0; i < num_arg_pages; i++) {
+   pos = ALIGN_DOWN(bprm->p, PAGE_SIZE) + i * PAGE_SIZE;
+   ret = get_user_pages_remote(current, bprm->mm, pos, 1,
+   FOLL_FORCE, , NULL, NULL);
+   if (ret <= 0) {
+   kfree(buf);
+   return -ENOMEM;
+   }
+
+   kaddr = kmap(page);
+   memcpy(buf + i * PAGE_SIZE, kaddr, PAGE_SIZE);
+   kunmap(page);
+   put_page(page);
+   }
+
+   ctx->arg_pages = buf;
+   ctx->num_arg_pages = num_arg_pages;
+   ctx->max_arg_offset = num_arg_pages * PAGE_SIZE;
+
+   return 0;
+}
+
 static int krsi_process_execution(struct linux_binprm *bprm)
 {
int ret;
@@ -26,7 +75,14 @@ static int krsi_process_execution(struct linux_binprm *bprm)
.bprm = bprm,
};
 
+   ret = pin_arg_pages(_ctx);
+   if (ret < 0)
+   goto out_arg_pages;
+
ret = krsi_run_progs(PROCESS_EXECUTION, );
+   kfree(ctx.bprm_ctx.arg_pages);
+
+out_arg_pages:
return ret;
 }
 
-- 
2.20.1



[RFC v1 12/14] krsi: Add an eBPF helper function to get the value of an env variable

2019-09-10 Thread KP Singh
From: KP Singh 

The helper returns the value of the environment variable in the buffer
that is passed to it. If the var is set multiple times, the helper
returns all the values as null separated strings.

If the buffer is too short for these values, the helper tries to fill it
the best it can and guarantees that the value returned in the buffer
is always null terminated. After the buffer is filled, the helper keeps
counting the number of times the environment variable is set in the
envp.

The return value of the helper is an u64 value which carries two pieces
of information.

  * The upper 32 bits are a u32 value signifying the number of times
the environment variable is set in the envp.
  * The lower 32 bits are a s32 value signifying the number of bytes
written to the buffer or an error code.

Since the value of the environment variable can be very long and exceed
what can be allocated on the BPF stack, a per-cpu array can be used
instead:

struct bpf_map_def SEC("maps") env_map = {
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
.key_size = sizeof(u32),
.value_size = 4096,
.max_entries = 1,
};

SEC("prgrm")
int bpf_prog1(void *ctx)
{
u32 map_id = 0;
u64 times_ret;
s32 ret;
char name[48] = "LD_PRELOAD";

char *map_value = bpf_map_lookup_elem(_map, _id);
if (!map_value)
return 0;

// Read the lower 32 bits for the return value
times_ret = krsi_get_env_var(ctx, name, 48, map_value, 4096);
ret = times_ret & 0x;
if (ret < 0)
return ret;
    return 0;
}

Signed-off-by: KP Singh 
---
 include/uapi/linux/bpf.h  |  42 ++-
 security/krsi/ops.c   | 129 ++
 tools/include/uapi/linux/bpf.h|  42 ++-
 tools/testing/selftests/bpf/bpf_helpers.h |   3 +
 4 files changed, 214 insertions(+), 2 deletions(-)

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 32ab38f1a2fe..a4ef07956e07 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -2715,6 +2715,45 @@ union bpf_attr {
  * **-EPERM** if no permission to send the *sig*.
  *
  * **-EAGAIN** if bpf program can try again.
+ *
+ * u64 krsi_get_env_var(void *ctx, char *name, char *buf,
+ * size_t name_len, size_t buf_len)
+ * Description
+ * This helper can be used as a part of the
+ * process_execution hook of the KRSI LSM in
+ * programs of type BPF_PROG_TYPE_KRSI.
+ *
+ * The helper returns the value of the environment
+ * variable with the provided "name" for process that's
+ * going to be executed in the passed buffer, "buf". If the var
+ * is set multiple times, the helper returns all
+ * the values as null separated strings.
+ *
+ * If the buffer is too short for these values, the helper
+ * tries to fill it the best it can and guarantees that the value
+ * returned in the buffer  is always null terminated.
+ * After the buffer is filled, the helper keeps counting the number
+ * of times the environment variable is set in the envp.
+ *
+ * Return:
+ *
+ * The return value of the helper is an u64 value
+ * which carries two pieces of information:
+ *
+ *The upper 32 bits are a u32 value signifying
+ *the number of times the environment variable
+ *is set in the envp.
+ *The lower 32 bits are an s32 value signifying
+ *the number of bytes written to the buffer or an error code:
+ *
+ * **-ENOMEM** if the kernel is unable to allocate memory
+ * for pinning the argv and envv.
+ *
+ * **-E2BIG** if the value is larger than the size of the
+ *destination buffer. The higher bits will still
+ *the number of times the variable was set in the envp.
+ *
+ * **-EINVAL** if name is not a NULL terminated string.
  */
 #define __BPF_FUNC_MAPPER(FN)  \
FN(unspec), \
@@ -2826,7 +2865,8 @@ union bpf_attr {
FN(strtoul),\
FN(sk_storage_get), \
FN(sk_storage_delete),  \
-   FN(send_signal),
+   FN(send_signal),\
+   FN(krsi_get_env_var),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
diff --git a/security/krsi/ops.c b/security/krsi/ops.c
index 1f4df920139c..1db94dfaac15 100644
--- a/security/krsi/ops.c
+++ b/security/krsi/ops.c
@@ -6,6 +6,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include "krsi_init.h"
 #include "krsi_fs.h&q

[RFC v1 13/14] krsi: Provide an example to read and log environment variables

2019-09-10 Thread KP Singh
From: KP Singh 

*  The program takes the name of an environment variable as an
   argument.
*  An eBPF program is loaded and attached to the
   process_execution hook.
*  The name of the environment variable passed is updated in a
   eBPF per-cpu map.
*  The eBPF program uses the krsi_get_env_var helper to get the
   the value of this variable and logs the result to the perf
   events buffer.
*  The user-space program listens to the perf events and prints
   the values.

Example execution:

./krsi LD_PRELOAD

[p_pid=123] LD_PRELOAD is not set
[p_pid=456] LD_PRELOAD=/lib/bad.so
[p_pid=789] WARNING! LD_PRELOAD is set 2 times
[p_pid=789] LD_PRELOAD=/lib/decoy.so
[p_pid=789] LD_PRELOAD=/lib/bad.so

In a separate session the following [1, 2, 3] exec system calls
are made where:

[1, 2, 3] char *argv[] = {"/bin/ls", 0};

[1] char *envp = {0};
[2] char *envp = {"LD_PRELOAD=/lib/bad.so", 0};
[3] char *envp = {"LD_PRELOAD=/lib/decoy.so, "LD_PRELOAD=/lib/bad.so", 
0};

This example demonstrates that user-space is free to choose the format
in which the data is logged and can use very specific helpers like
krsi_get_env_var to populate only the data that is required.

Signed-off-by: KP Singh 
---
 MAINTAINERS|   3 +
 samples/bpf/.gitignore |   1 +
 samples/bpf/Makefile   |   3 +
 samples/bpf/krsi_helpers.h |  31 ++
 samples/bpf/krsi_kern.c|  52 ++
 samples/bpf/krsi_user.c| 202 +
 6 files changed, 292 insertions(+)
 create mode 100644 samples/bpf/krsi_helpers.h
 create mode 100644 samples/bpf/krsi_kern.c
 create mode 100644 samples/bpf/krsi_user.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 8e0364391d8b..ec378abb4c23 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9005,6 +9005,9 @@ F:kernel/kprobes.c
 KRSI SECURITY MODULE
 M: KP Singh 
 S: Supported
+F: samples/bpf/krsi_helpers.h
+F: samples/bpf/krsi_kern.c
+F: samples/bpf/krsi_user.c
 F: security/krsi/
 
 KS0108 LCD CONTROLLER DRIVER
diff --git a/samples/bpf/.gitignore b/samples/bpf/.gitignore
index 74d31fd3c99c..6bbf5a04877f 100644
--- a/samples/bpf/.gitignore
+++ b/samples/bpf/.gitignore
@@ -2,6 +2,7 @@ cpustat
 fds_example
 hbm
 ibumad
+krsi
 lathist
 lwt_len_hist
 map_perf_test
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 1d9be26b4edd..33d3bef17549 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -8,6 +8,7 @@ hostprogs-y := test_lru_dist
 hostprogs-y += sock_example
 hostprogs-y += fds_example
 hostprogs-y += sockex1
+hostprogs-y += krsi
 hostprogs-y += sockex2
 hostprogs-y += sockex3
 hostprogs-y += tracex1
@@ -62,6 +63,7 @@ TRACE_HELPERS := 
../../tools/testing/selftests/bpf/trace_helpers.o
 
 fds_example-objs := fds_example.o
 sockex1-objs := sockex1_user.o
+krsi-objs := krsi_user.o $(TRACE_HELPERS)
 sockex2-objs := sockex2_user.o
 sockex3-objs := bpf_load.o sockex3_user.o
 tracex1-objs := bpf_load.o tracex1_user.o
@@ -113,6 +115,7 @@ hbm-objs := bpf_load.o hbm.o $(CGROUP_HELPERS)
 # Tell kbuild to always build the programs
 always := $(hostprogs-y)
 always += sockex1_kern.o
+always += krsi_kern.o
 always += sockex2_kern.o
 always += sockex3_kern.o
 always += tracex1_kern.o
diff --git a/samples/bpf/krsi_helpers.h b/samples/bpf/krsi_helpers.h
new file mode 100644
index ..3007bfd6212e
--- /dev/null
+++ b/samples/bpf/krsi_helpers.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _KRSI_HELPERS_H
+#define _KRSI_HELPERS_H
+
+#define __bpf_percpu_val_align __aligned(8)
+
+#define ENV_VAR_NAME_MAX_LEN 48
+#define ENV_VAR_VAL_MAX_LEN 4096
+
+#define MAX_CPUS 128
+
+#define __LOWER(x) (x & 0x)
+#define __UPPER(x) (x >> 32)
+
+struct krsi_env_value {
+   // The name of the environment variable.
+   char name[ENV_VAR_NAME_MAX_LEN];
+   // The value of the environment variable (if set).
+   char value[ENV_VAR_VAL_MAX_LEN];
+   // Indicates if an overflow occurred while reading the value of the
+   // of the environment variable. This means that an -E2BIG was received
+   // from the krsi_get_env_var helper.
+   bool overflow;
+   // The number of times the environment variable was set.
+   __u32 times;
+   // The PID of the parent process.
+   __u32 p_pid;
+} __bpf_percpu_val_align;
+
+#endif // _KRSI_HELPERS_H
diff --git a/samples/bpf/krsi_kern.c b/samples/bpf/krsi_kern.c
new file mode 100644
index ..087a6f0cc81d
--- /dev/null
+++ b/samples/bpf/krsi_kern.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include 
+#include 
+#include 
+#include "bpf_helpers.h"
+#include "krsi_helpers.h"
+
+#define MAX_CPUS 128
+
+struct bpf_map_def SEC("maps") env_map = {
+   .type = BPF_

[RFC v1 14/14] krsi: Pin arg pages only when needed

2019-09-10 Thread KP Singh
From: KP Singh 

Adds a callback which is called when a new program is attached
to a hook. The callback registered by the process_exection hook
checks if a program that has calls to a helper that requires pages to
be pinned (eg. krsi_get_env_var).

Signed-off-by: KP Singh 
---
 include/linux/krsi.h  |  1 +
 security/krsi/include/hooks.h |  5 ++-
 security/krsi/include/krsi_init.h |  7 
 security/krsi/krsi.c  | 62 ---
 security/krsi/ops.c   | 10 -
 5 files changed, 77 insertions(+), 8 deletions(-)

diff --git a/include/linux/krsi.h b/include/linux/krsi.h
index c7d1790d0c1f..e443d0309764 100644
--- a/include/linux/krsi.h
+++ b/include/linux/krsi.h
@@ -7,6 +7,7 @@
 
 #ifdef CONFIG_SECURITY_KRSI
 int krsi_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog);
+extern const struct bpf_func_proto krsi_get_env_var_proto;
 #else
 static inline int krsi_prog_attach(const union bpf_attr *attr,
   struct bpf_prog *prog)
diff --git a/security/krsi/include/hooks.h b/security/krsi/include/hooks.h
index e070c452b5de..38293125ff99 100644
--- a/security/krsi/include/hooks.h
+++ b/security/krsi/include/hooks.h
@@ -8,7 +8,7 @@
  *
  * Format:
  *
- *   KRSI_HOOK_INIT(TYPE, NAME, LSM_HOOK, KRSI_HOOK_FN)
+ *   KRSI_HOOK_INIT(TYPE, NAME, LSM_HOOK, KRSI_HOOK_FN, CALLBACK)
  *
  * KRSI adds one layer of indirection between the name of the hook and the name
  * it exposes to the userspace in Security FS to prevent the userspace from
@@ -18,4 +18,5 @@
 KRSI_HOOK_INIT(PROCESS_EXECUTION,
   process_execution,
   bprm_check_security,
-  krsi_process_execution)
+  krsi_process_execution,
+  krsi_process_execution_cb)
diff --git a/security/krsi/include/krsi_init.h 
b/security/krsi/include/krsi_init.h
index 6152847c3b08..99801d5b273a 100644
--- a/security/krsi/include/krsi_init.h
+++ b/security/krsi/include/krsi_init.h
@@ -31,6 +31,8 @@ struct krsi_ctx {
};
 };
 
+typedef int (*krsi_prog_attach_t) (struct bpf_prog_array *);
+
 /*
  * The LSM creates one file per hook.
  *
@@ -61,6 +63,11 @@ struct krsi_hook {
 * The eBPF programs that are attached to this hook.
 */
struct bpf_prog_array __rcu *progs;
+   /*
+* The attach callback is called before a new program is attached
+* to the hook and is passed the updated bpf_prog_array as an argument.
+*/
+   krsi_prog_attach_t attach_callback;
 };
 
 extern struct krsi_hook krsi_hooks_list[];
diff --git a/security/krsi/krsi.c b/security/krsi/krsi.c
index 00a7150c1b22..a4443d7aa150 100644
--- a/security/krsi/krsi.c
+++ b/security/krsi/krsi.c
@@ -5,15 +5,65 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include "krsi_init.h"
 
+/*
+ * need_arg_pages is only updated in bprm_check_security_cb
+ * when a mutex on krsi_hook for bprm_check_security is already
+ * held. need_arg_pages avoids pinning pages when no program
+ * that needs them is attached to the hook.
+ */
+static bool need_arg_pages;
+
+/*
+ * Checks if the instruction is a BPF_CALL to an eBPF helper located
+ * at the given address.
+ */
+static inline bool bpf_is_call_to_func(struct bpf_insn *insn,
+  void *func_addr)
+{
+   u8 opcode = BPF_OP(insn->code);
+
+   if (opcode != BPF_CALL)
+   return false;
+
+   if (insn->src_reg == BPF_PSEUDO_CALL)
+   return false;
+
+   /*
+* The BPF verifier updates the value of insn->imm from the
+* enum bpf_func_id to the offset of the address of helper
+* from the __bpf_call_base.
+*/
+   return __bpf_call_base + insn->imm == func_addr;
+}
+
+static int krsi_process_execution_cb(struct bpf_prog_array *array)
+{
+   struct bpf_prog_array_item *item = array->items;
+   struct bpf_prog *p;
+   const struct bpf_func_proto *proto = _get_env_var_proto;
+   int i;
+
+   while ((p = READ_ONCE(item->prog))) {
+   for (i = 0; i < p->len; i++) {
+   if (bpf_is_call_to_func(>insnsi[i], proto->func))
+   need_arg_pages = true;
+   }
+   item++;
+   }
+   return 0;
+}
+
 struct krsi_hook krsi_hooks_list[] = {
-   #define KRSI_HOOK_INIT(TYPE, NAME, H, I) \
+   #define KRSI_HOOK_INIT(TYPE, NAME, H, I, CB) \
[TYPE] = { \
.h_type = TYPE, \
.name = #NAME, \
+   .attach_callback = CB, \
},
#include "hooks.h"
#undef KRSI_HOOK_INIT
@@ -75,9 +125,11 @@ static int krsi_process_execution(struct linux_binprm *bprm)
.bprm = bprm,
};
 
-   ret = pin_arg_pages(_ctx);
-   if (ret < 0)
-   goto out_arg_pages;
+   if (READ_ONCE(need_a

[RFC v1 09/14] krsi: Add a helper function for bpf_perf_event_output

2019-09-10 Thread KP Singh
From: KP Singh 

This helper is mapped to the existing operation
BPF_FUNC_perf_event_output.

An example usage of this function would be:

#define BUF_SIZE 64;

struct bpf_map_def SEC("maps") perf_map = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(u32),
.max_entries = MAX_CPUS,
};

SEC("krsi")
int bpf_prog1(void *ctx)
{
char buf[BUF_SIZE];
int len;
u64 flags = BPF_F_CURRENT_CPU;

/* some logic that fills up buf with len data*/
len = fill_up_buf(buf);
if (len < 0)
return len;
if (len > BU)
return 0;

bpf_perf_event_output(ctx, _map, flags, buf len);
return 0;
}

A sample program that showcases the use of bpf_perf_event_output is
added later.

Signed-off-by: KP Singh 
---
 security/krsi/ops.c | 22 ++
 1 file changed, 22 insertions(+)

diff --git a/security/krsi/ops.c b/security/krsi/ops.c
index a61508b7018f..57bd304a03f4 100644
--- a/security/krsi/ops.c
+++ b/security/krsi/ops.c
@@ -111,6 +111,26 @@ static bool krsi_prog_is_valid_access(int off, int size,
return false;
 }
 
+BPF_CALL_5(krsi_event_output, void *, log,
+  struct bpf_map *, map, u64, flags, void *, data, u64, size)
+{
+   if (unlikely(flags & ~(BPF_F_INDEX_MASK)))
+   return -EINVAL;
+
+   return bpf_event_output(map, flags, data, size, NULL, 0, NULL);
+}
+
+static const struct bpf_func_proto krsi_event_output_proto =  {
+   .func   = krsi_event_output,
+   .gpl_only   = true,
+   .ret_type   = RET_INTEGER,
+   .arg1_type  = ARG_PTR_TO_CTX,
+   .arg2_type  = ARG_CONST_MAP_PTR,
+   .arg3_type  = ARG_ANYTHING,
+   .arg4_type  = ARG_PTR_TO_MEM,
+   .arg5_type  = ARG_CONST_SIZE_OR_ZERO,
+};
+
 static const struct bpf_func_proto *krsi_prog_func_proto(enum bpf_func_id
 func_id,
 const struct bpf_prog
@@ -121,6 +141,8 @@ static const struct bpf_func_proto 
*krsi_prog_func_proto(enum bpf_func_id
return _map_lookup_elem_proto;
case BPF_FUNC_get_current_pid_tgid:
return _get_current_pid_tgid_proto;
+   case BPF_FUNC_perf_event_output:
+   return _event_output_proto;
default:
return NULL;
}
-- 
2.20.1



[RFC v1 03/14] bpf: krsi: sync BPF UAPI header with tools

2019-09-10 Thread KP Singh
From: KP Singh 

Signed-off-by: KP Singh 
---
 tools/include/uapi/linux/bpf.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index a5aa7d3ac6a1..32ab38f1a2fe 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -171,6 +171,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_CGROUP_SYSCTL,
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
BPF_PROG_TYPE_CGROUP_SOCKOPT,
+   BPF_PROG_TYPE_KRSI,
 };
 
 enum bpf_attach_type {
@@ -197,6 +198,7 @@ enum bpf_attach_type {
BPF_CGROUP_UDP6_RECVMSG,
BPF_CGROUP_GETSOCKOPT,
BPF_CGROUP_SETSOCKOPT,
+   BPF_KRSI,
__MAX_BPF_ATTACH_TYPE
 };
 
-- 
2.20.1



[RFC v1 02/14] krsi: Introduce types for KRSI eBPF

2019-09-10 Thread KP Singh
From: KP Singh 

KRSI intrdocues a new eBPF program type BPF_PROG_TYPE_KRSI with an
expected attach type of BPF_KRSI. An -EINVAL error is returned if an
attachment is requested.

Signed-off-by: KP Singh 
---
 include/linux/bpf_types.h |  3 +++
 include/uapi/linux/bpf.h  |  2 ++
 kernel/bpf/syscall.c  |  6 ++
 security/krsi/Makefile|  2 +-
 security/krsi/ops.c   | 10 ++
 5 files changed, 22 insertions(+), 1 deletion(-)
 create mode 100644 security/krsi/ops.c

diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index eec5aeeeaf92..129594c09b5c 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -38,6 +38,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_LIRC_MODE2, lirc_mode2)
 #ifdef CONFIG_INET
 BPF_PROG_TYPE(BPF_PROG_TYPE_SK_REUSEPORT, sk_reuseport)
 #endif
+#ifdef CONFIG_SECURITY_KRSI
+BPF_PROG_TYPE(BPF_PROG_TYPE_KRSI, krsi)
+#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/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index a5aa7d3ac6a1..32ab38f1a2fe 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -171,6 +171,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_CGROUP_SYSCTL,
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
BPF_PROG_TYPE_CGROUP_SOCKOPT,
+   BPF_PROG_TYPE_KRSI,
 };
 
 enum bpf_attach_type {
@@ -197,6 +198,7 @@ enum bpf_attach_type {
BPF_CGROUP_UDP6_RECVMSG,
BPF_CGROUP_GETSOCKOPT,
BPF_CGROUP_SETSOCKOPT,
+   BPF_KRSI,
__MAX_BPF_ATTACH_TYPE
 };
 
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 5d141f16f6fa..f38a539f7e67 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -1915,6 +1915,9 @@ static int bpf_prog_attach(const union bpf_attr *attr)
case BPF_LIRC_MODE2:
ptype = BPF_PROG_TYPE_LIRC_MODE2;
break;
+   case BPF_KRSI:
+   ptype = BPF_PROG_TYPE_KRSI;
+   break;
case BPF_FLOW_DISSECTOR:
ptype = BPF_PROG_TYPE_FLOW_DISSECTOR;
break;
@@ -1946,6 +1949,9 @@ static int bpf_prog_attach(const union bpf_attr *attr)
case BPF_PROG_TYPE_LIRC_MODE2:
ret = lirc_prog_attach(attr, prog);
break;
+   case BPF_PROG_TYPE_KRSI:
+   ret = -EINVAL;
+   break;
case BPF_PROG_TYPE_FLOW_DISSECTOR:
ret = skb_flow_dissector_bpf_prog_attach(attr, prog);
break;
diff --git a/security/krsi/Makefile b/security/krsi/Makefile
index 73320e8d16f8..660cc1f422fd 100644
--- a/security/krsi/Makefile
+++ b/security/krsi/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_SECURITY_KRSI) := krsi.o
+obj-$(CONFIG_SECURITY_KRSI) := krsi.o ops.o
diff --git a/security/krsi/ops.c b/security/krsi/ops.c
new file mode 100644
index ..f2de3bd9621e
--- /dev/null
+++ b/security/krsi/ops.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include 
+#include 
+
+const struct bpf_prog_ops krsi_prog_ops = {
+};
+
+const struct bpf_verifier_ops krsi_verifier_ops = {
+};
-- 
2.20.1



[RFC v1 08/14] krsi: Show attached program names in hook read handler.

2019-09-10 Thread KP Singh
From: KP Singh 

For inspectability the system administrator should be able to view the
list of active KRSI programs:

   bash # cat /sys/kernel/security/krsi/process_execution
   bpf_prog1

Signed-off-by: KP Singh 
---
 security/krsi/krsi_fs.c | 76 -
 1 file changed, 75 insertions(+), 1 deletion(-)

diff --git a/security/krsi/krsi_fs.c b/security/krsi/krsi_fs.c
index 3ba18b52ce85..0ebf4fabe935 100644
--- a/security/krsi/krsi_fs.c
+++ b/security/krsi/krsi_fs.c
@@ -6,6 +6,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 
@@ -16,8 +17,81 @@ extern struct krsi_hook krsi_hooks_list[];
 
 static struct dentry *krsi_dir;
 
+static void *seq_start(struct seq_file *m, loff_t *pos)
+   __acquires(rcu)
+{
+   struct krsi_hook *h;
+   struct dentry *dentry;
+   struct bpf_prog_array *progs;
+   struct bpf_prog_array_item *item;
+
+   /*
+* rcu_read_lock() must be held before any return statement
+* because the stop() will always be called and thus call
+* rcu_read_unlock()
+*/
+   rcu_read_lock();
+
+   dentry = file_dentry(m->file);
+   h = dentry->d_fsdata;
+   if (WARN_ON(!h))
+   return ERR_PTR(-EFAULT);
+
+   progs = rcu_dereference(h->progs);
+   if ((*pos) >= bpf_prog_array_length(progs))
+   return NULL;
+
+   item = progs->items + *pos;
+   if (!item->prog)
+   return NULL;
+
+   return item;
+}
+
+static void *seq_next(struct seq_file *m, void *v, loff_t *pos)
+{
+   struct bpf_prog_array_item *item = v;
+
+   item++;
+   ++*pos;
+
+   if (!item->prog)
+   return NULL;
+
+   return item;
+}
+
+static void seq_stop(struct seq_file *m, void *v)
+   __releases(rcu)
+{
+   rcu_read_unlock();
+}
+
+static int show_prog(struct seq_file *m, void *v)
+{
+   struct bpf_prog_array_item *item = v;
+
+   seq_printf(m, "%s\n", item->prog->aux->name);
+   return 0;
+}
+
+static const struct seq_operations seq_ops = {
+   .show   = show_prog,
+   .start  = seq_start,
+   .next   = seq_next,
+   .stop   = seq_stop,
+};
+
+static int hook_open(struct inode *inode, struct file *file)
+{
+   return seq_open(file, _ops);
+}
+
 static const struct file_operations krsi_hook_ops = {
-   .llseek = generic_file_llseek,
+   .open   = hook_open,
+   .read   = seq_read,
+   .llseek = seq_lseek,
+   .release= seq_release,
 };
 
 int krsi_fs_initialized;
-- 
2.20.1



[RFC v1 04/14] krsi: Add support in libbpf for BPF_PROG_TYPE_KRSI

2019-09-10 Thread KP Singh
From: KP Singh 

Update the libbpf library with functionality to load and
attach a program type BPF_PROG_TYPE_KRSI.

Since the bpf_prog_load does not allow the specification of an
expected attach type, it's recommended to use bpf_prog_load_xattr and
set the expected attach type as KRSI.

Signed-off-by: KP Singh 
---
 tools/lib/bpf/libbpf.c| 4 
 tools/lib/bpf/libbpf.h| 2 ++
 tools/lib/bpf/libbpf.map  | 2 ++
 tools/lib/bpf/libbpf_probes.c | 1 +
 4 files changed, 9 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 2b57d7ea7836..3cc86bbc68cd 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -2676,6 +2676,7 @@ static bool bpf_prog_type__needs_kver(enum bpf_prog_type 
type)
case BPF_PROG_TYPE_PERF_EVENT:
case BPF_PROG_TYPE_CGROUP_SYSCTL:
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
+   case BPF_PROG_TYPE_KRSI:
return false;
case BPF_PROG_TYPE_KPROBE:
default:
@@ -3536,6 +3537,7 @@ bool bpf_program__is_##NAME(const struct bpf_program 
*prog)   \
 }  \
 
 BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER);
+BPF_PROG_TYPE_FNS(krsi, BPF_PROG_TYPE_KRSI);
 BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);
 BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS);
 BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT);
@@ -3590,6 +3592,8 @@ static const struct {
BPF_PROG_SEC("lwt_out", BPF_PROG_TYPE_LWT_OUT),
BPF_PROG_SEC("lwt_xmit",BPF_PROG_TYPE_LWT_XMIT),
BPF_PROG_SEC("lwt_seg6local",   BPF_PROG_TYPE_LWT_SEG6LOCAL),
+   BPF_APROG_SEC("krsi",   BPF_PROG_TYPE_KRSI,
+   BPF_KRSI),
BPF_APROG_SEC("cgroup_skb/ingress", BPF_PROG_TYPE_CGROUP_SKB,
BPF_CGROUP_INET_INGRESS),
BPF_APROG_SEC("cgroup_skb/egress",  BPF_PROG_TYPE_CGROUP_SKB,
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 5cbf459ece0b..8781d29b4035 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -261,6 +261,7 @@ LIBBPF_API int bpf_program__set_sched_cls(struct 
bpf_program *prog);
 LIBBPF_API int bpf_program__set_sched_act(struct bpf_program *prog);
 LIBBPF_API int bpf_program__set_xdp(struct bpf_program *prog);
 LIBBPF_API int bpf_program__set_perf_event(struct bpf_program *prog);
+LIBBPF_API int bpf_program__set_krsi(struct bpf_program *prog);
 LIBBPF_API void bpf_program__set_type(struct bpf_program *prog,
  enum bpf_prog_type type);
 LIBBPF_API void
@@ -275,6 +276,7 @@ LIBBPF_API bool bpf_program__is_sched_cls(const struct 
bpf_program *prog);
 LIBBPF_API bool bpf_program__is_sched_act(const struct bpf_program *prog);
 LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog);
 LIBBPF_API bool bpf_program__is_perf_event(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_krsi(const struct bpf_program *prog);
 
 /*
  * No need for __attribute__((packed)), all members of 'bpf_map_def'
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index f9d316e873d8..75b8fe419c11 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -68,6 +68,7 @@ LIBBPF_0.0.1 {
bpf_prog_test_run_xattr;
bpf_program__fd;
bpf_program__is_kprobe;
+   bpf_program__is_krsi;
bpf_program__is_perf_event;
bpf_program__is_raw_tracepoint;
bpf_program__is_sched_act;
@@ -85,6 +86,7 @@ LIBBPF_0.0.1 {
bpf_program__set_expected_attach_type;
bpf_program__set_ifindex;
bpf_program__set_kprobe;
+   bpf_program__set_krsi;
bpf_program__set_perf_event;
bpf_program__set_prep;
bpf_program__set_priv;
diff --git a/tools/lib/bpf/libbpf_probes.c b/tools/lib/bpf/libbpf_probes.c
index ace1a0708d99..cc515a36794d 100644
--- a/tools/lib/bpf/libbpf_probes.c
+++ b/tools/lib/bpf/libbpf_probes.c
@@ -102,6 +102,7 @@ probe_load(enum bpf_prog_type prog_type, const struct 
bpf_insn *insns,
case BPF_PROG_TYPE_FLOW_DISSECTOR:
case BPF_PROG_TYPE_CGROUP_SYSCTL:
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
+   case BPF_PROG_TYPE_KRSI:
default:
break;
}
-- 
2.20.1



<    1   2   3   >