]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bpf: Implement exclusive map creation
authorKP Singh <kpsingh@kernel.org>
Sun, 14 Sep 2025 21:51:31 +0000 (23:51 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 19 Sep 2025 02:11:42 +0000 (19:11 -0700)
Exclusive maps allow maps to only be accessed by program with a
program with a matching hash which is specified in the excl_prog_hash
attr.

For the signing use-case, this allows the trusted loader program
to load the map and verify the integrity

Signed-off-by: KP Singh <kpsingh@kernel.org>
Link: https://lore.kernel.org/r/20250914215141.15144-3-kpsingh@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/bpf.h
include/uapi/linux/bpf.h
kernel/bpf/syscall.c
kernel/bpf/verifier.c
tools/include/uapi/linux/bpf.h

index d75902074bd1f3375ee7fd97fe8aafdbb642da9c..c6a6ee1b29388dfc30d9ee6ddc5aab14ed110e59 100644 (file)
@@ -329,6 +329,7 @@ struct bpf_map {
        atomic64_t sleepable_refcnt;
        s64 __percpu *elem_count;
        u64 cookie; /* write-once */
+       char *excl_prog_sha;
 };
 
 static inline const char *btf_field_type_name(enum btf_field_type type)
index 233de8677382ec9c53b17858618934b7cee35be6..57687b2e1c4770b48dd04cd7b7a840b87e74960f 100644 (file)
@@ -1522,6 +1522,12 @@ union bpf_attr {
                 * If provided, map_flags should have BPF_F_TOKEN_FD flag set.
                 */
                __s32   map_token_fd;
+
+               /* Hash of the program that has exclusive access to the map.
+                */
+               __aligned_u64 excl_prog_hash;
+               /* Size of the passed excl_prog_hash. */
+               __u32 excl_prog_hash_size;
        };
 
        struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */
index 3f178a0f8eb12c24d68b95d53114c1749d5a3b2a..c8ef91acfe98aa96c7602cf34c505927fb87444b 100644 (file)
@@ -860,6 +860,7 @@ static void bpf_map_free(struct bpf_map *map)
         * the free of values or special fields allocated from bpf memory
         * allocator.
         */
+       kfree(map->excl_prog_sha);
        migrate_disable();
        map->ops->map_free(map);
        migrate_enable();
@@ -1338,9 +1339,9 @@ static bool bpf_net_capable(void)
        return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN);
 }
 
-#define BPF_MAP_CREATE_LAST_FIELD map_token_fd
+#define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size
 /* called via syscall */
-static int map_create(union bpf_attr *attr, bool kernel)
+static int map_create(union bpf_attr *attr, bpfptr_t uattr)
 {
        const struct bpf_map_ops *ops;
        struct bpf_token *token = NULL;
@@ -1534,7 +1535,29 @@ static int map_create(union bpf_attr *attr, bool kernel)
                        attr->btf_vmlinux_value_type_id;
        }
 
-       err = security_bpf_map_create(map, attr, token, kernel);
+       if (attr->excl_prog_hash) {
+               bpfptr_t uprog_hash = make_bpfptr(attr->excl_prog_hash, uattr.is_kernel);
+
+               if (attr->excl_prog_hash_size != SHA256_DIGEST_SIZE) {
+                       err = -EINVAL;
+                       goto free_map;
+               }
+
+               map->excl_prog_sha = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL);
+               if (!map->excl_prog_sha) {
+                       err = -ENOMEM;
+                       goto free_map;
+               }
+
+               if (copy_from_bpfptr(map->excl_prog_sha, uprog_hash, SHA256_DIGEST_SIZE)) {
+                       err = -EFAULT;
+                       goto free_map;
+               }
+       } else if (attr->excl_prog_hash_size) {
+               return -EINVAL;
+       }
+
+       err = security_bpf_map_create(map, attr, token, uattr.is_kernel);
        if (err)
                goto free_map_sec;
 
@@ -6008,7 +6031,7 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
 
        switch (cmd) {
        case BPF_MAP_CREATE:
-               err = map_create(&attr, uattr.is_kernel);
+               err = map_create(&attr, uattr);
                break;
        case BPF_MAP_LOOKUP_ELEM:
                err = map_lookup_elem(&attr);
index 6625570ac23de25553a70ba97cb41ed01143492b..aef6b266f08d1f913d6f789fccea86776fb8355a 100644 (file)
@@ -20407,6 +20407,12 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,
 {
        enum bpf_prog_type prog_type = resolve_prog_type(prog);
 
+       if (map->excl_prog_sha &&
+           memcmp(map->excl_prog_sha, prog->digest, SHA256_DIGEST_SIZE)) {
+               verbose(env, "program's hash doesn't match map's excl_prog_hash\n");
+               return -EACCES;
+       }
+
        if (btf_record_has_field(map->record, BPF_LIST_HEAD) ||
            btf_record_has_field(map->record, BPF_RB_ROOT)) {
                if (is_tracing_prog_type(prog_type)) {
index 233de8677382ec9c53b17858618934b7cee35be6..57687b2e1c4770b48dd04cd7b7a840b87e74960f 100644 (file)
@@ -1522,6 +1522,12 @@ union bpf_attr {
                 * If provided, map_flags should have BPF_F_TOKEN_FD flag set.
                 */
                __s32   map_token_fd;
+
+               /* Hash of the program that has exclusive access to the map.
+                */
+               __aligned_u64 excl_prog_hash;
+               /* Size of the passed excl_prog_hash. */
+               __u32 excl_prog_hash_size;
        };
 
        struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */