From nobody Wed Jul 1 01:55:01 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 58349C433F5 for ; Wed, 5 Jan 2022 03:03:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237152AbiAEDDz (ORCPT ); Tue, 4 Jan 2022 22:03:55 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56796 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229584AbiAEDDw (ORCPT ); Tue, 4 Jan 2022 22:03:52 -0500 Received: from mail-pj1-x1030.google.com (mail-pj1-x1030.google.com [IPv6:2607:f8b0:4864:20::1030]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 878B5C061761; Tue, 4 Jan 2022 19:03:52 -0800 (PST) Received: by mail-pj1-x1030.google.com with SMTP id ie13so215973pjb.1; Tue, 04 Jan 2022 19:03:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=lcFqDTjeFGBwrZNHRnNh9af0ehtq+KJ9lCMfNLza1NE=; b=CuRpkgBnmNWJkVle0EcTSB3MZ6woiP1lAc33eEWdhMadZC/YMZI7eWBgWqfyagzP6p dgJtZUbZSRnO+nVmnPJuprFzKjzzucFDTK4HQ7fgdLplNJ9BzuGh1+uYcHAqD1BjrpNC PHW1JpXE9Moq6sKom9n9SZGmt8DRahGMCnMBeOd4Mmts6DwbLljVkHAP30WUsA2317MY SVCAhZcpwwLYPLg2tKfWNd8zacXA9Tee83qegyOIFcI6glHy3r0yWDhxdM6mzmbRn5PT qYVgzWTPhGBGZxStTBBU9QCMZ703kUXIfAHLvI+3yIl108Rb2cUsLrtFv1Tt9w7TVRcL Y1uw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=lcFqDTjeFGBwrZNHRnNh9af0ehtq+KJ9lCMfNLza1NE=; b=hoG5D612JC6gIx8D77HJ6SNrwssk96GO17xnHK7We6faIRYohh6Zq4qXf4/HznF6mT o9FA49omFUmzRGCot8FGFzYbvtxx4fC5rFKDXQ3gGIF+R0qkL7J49jxKGGLLlK1w+dvL ixaJaLcbBwzio4cffMqFEKiqGpL0ZnwmevlEcP0URB9UrAX2PfQJF25vhliGm3XheOM3 /6FoI/9IRxXAPWJjomXV2GXk7bsoyicU3coSX3xQXb6wN9H+JzWbfSwPmtJ+tZ1rLtO/ hVoDzuDlHbgtvE3q4byZQNa2jUAp+M9f1eC43KO0ZUhe8JSmSr3auDHKM7muqXCCqhrZ MWAA== X-Gm-Message-State: AOAM533wno7NSm9LQf6od3rJUKbbWKV+uFtwnxYUyqD+nDR61Y+l0Ukt 6z5yNXNdOMmlm/2OwrlWyw== X-Google-Smtp-Source: ABdhPJxIo+SONptg1STxL0lc/7tny0XTUob1ArKCEwwXH1A7fK9ux1xBkyE5TTu0mQu0cvAF/2tuPA== X-Received: by 2002:a17:90b:3903:: with SMTP id ob3mr1720071pjb.178.1641351832027; Tue, 04 Jan 2022 19:03:52 -0800 (PST) Received: from jevburton2.c.googlers.com.com (157.214.185.35.bc.googleusercontent.com. [35.185.214.157]) by smtp.gmail.com with ESMTPSA id i9sm34280818pgc.27.2022.01.04.19.03.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Jan 2022 19:03:51 -0800 (PST) From: Joe Burton To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, ppenkov@google.com, sdf@google.com, haoluo@google.com Cc: Joe Burton Subject: [PATCH bpf-next v4 1/3] bpf: Add map tracing functions and call sites Date: Wed, 5 Jan 2022 03:03:43 +0000 Message-Id: <20220105030345.3255846-2-jevburton.kernel@gmail.com> X-Mailer: git-send-email 2.34.1.448.ga2b2bfdf31-goog In-Reply-To: <20220105030345.3255846-1-jevburton.kernel@gmail.com> References: <20220105030345.3255846-1-jevburton.kernel@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Joe Burton Add two functions that fentry/fexit/fmod_ret programs can attach to: bpf_map_trace_update_elem bpf_map_trace_delete_elem These functions have the same arguments as bpf_map_{update,delete}_elem. Invoke these functions from the following map types: BPF_MAP_TYPE_ARRAY BPF_MAP_TYPE_PERCPU_ARRAY BPF_MAP_TYPE_HASH BPF_MAP_TYPE_PERCPU_HASH BPF_MAP_TYPE_LRU_HASH BPF_MAP_TYPE_LRU_PERCPU_HASH The only guarantee about these functions is that they are invoked before the corresponding action occurs. Other conditions may prevent the corresponding action from occurring after the function is invoked. Signed-off-by: Joe Burton --- kernel/bpf/Makefile | 2 +- kernel/bpf/arraymap.c | 4 +++- kernel/bpf/hashtab.c | 20 +++++++++++++++++++- kernel/bpf/map_trace.c | 17 +++++++++++++++++ kernel/bpf/map_trace.h | 19 +++++++++++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 kernel/bpf/map_trace.c create mode 100644 kernel/bpf/map_trace.h diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index c1a9be6a4b9f..0cf38dab339a 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -9,7 +9,7 @@ CFLAGS_core.o +=3D $(call cc-disable-warning, override-init= ) $(cflags-nogcse-yy) obj-$(CONFIG_BPF_SYSCALL) +=3D syscall.o verifier.o inode.o helpers.o tnum= .o bpf_iter.o map_iter.o task_iter.o prog_iter.o obj-$(CONFIG_BPF_SYSCALL) +=3D hashtab.o arraymap.o percpu_freelist.o bpf_= lru_list.o lpm_trie.o map_in_map.o bloom_filter.o obj-$(CONFIG_BPF_SYSCALL) +=3D local_storage.o queue_stack_maps.o ringbuf.o -obj-$(CONFIG_BPF_SYSCALL) +=3D bpf_local_storage.o bpf_task_storage.o +obj-$(CONFIG_BPF_SYSCALL) +=3D bpf_local_storage.o bpf_task_storage.o map_= trace.o obj-${CONFIG_BPF_LSM} +=3D bpf_inode_storage.o obj-$(CONFIG_BPF_SYSCALL) +=3D disasm.o obj-$(CONFIG_BPF_JIT) +=3D trampoline.o diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index c7a5be3bf8be..e9e7bd27ffad 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -13,6 +13,7 @@ #include =20 #include "map_in_map.h" +#include "map_trace.h" =20 #define ARRAY_CREATE_FLAG_MASK \ (BPF_F_NUMA_NODE | BPF_F_MMAPABLE | BPF_F_ACCESS_MASK | \ @@ -329,7 +330,8 @@ static int array_map_update_elem(struct bpf_map *map, v= oid *key, void *value, copy_map_value(map, val, value); check_and_free_timer_in_array(array, val); } - return 0; + + return bpf_map_trace_update_elem(map, key, value, map_flags); } =20 int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value, diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index d29af9988f37..8fb19ed707e8 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -13,6 +13,7 @@ #include "percpu_freelist.h" #include "bpf_lru_list.h" #include "map_in_map.h" +#include "map_trace.h" =20 #define HTAB_CREATE_FLAG_MASK \ (BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE | \ @@ -1055,7 +1056,8 @@ static int htab_map_update_elem(struct bpf_map *map, = void *key, void *value, copy_map_value_locked(map, l_old->key + round_up(key_size, 8), value, false); - return 0; + return bpf_map_trace_update_elem(map, key, value, + map_flags); } /* fall through, grab the bucket lock and lookup again. * 99.9% chance that the element won't be found, @@ -1109,6 +1111,8 @@ static int htab_map_update_elem(struct bpf_map *map, = void *key, void *value, ret =3D 0; err: htab_unlock_bucket(htab, b, hash, flags); + if (!ret) + ret =3D bpf_map_trace_update_elem(map, key, value, map_flags); return ret; } =20 @@ -1133,6 +1137,10 @@ static int htab_lru_map_update_elem(struct bpf_map *= map, void *key, void *value, /* unknown flags */ return -EINVAL; =20 + ret =3D bpf_map_trace_update_elem(map, key, value, map_flags); + if (unlikely(ret)) + return ret; + WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && !rcu_read_lock_bh_held()); =20 @@ -1182,6 +1190,8 @@ static int htab_lru_map_update_elem(struct bpf_map *m= ap, void *key, void *value, else if (l_old) htab_lru_push_free(htab, l_old); =20 + if (!ret) + ret =3D bpf_map_trace_update_elem(map, key, value, map_flags); return ret; } =20 @@ -1237,6 +1247,8 @@ static int __htab_percpu_map_update_elem(struct bpf_m= ap *map, void *key, ret =3D 0; err: htab_unlock_bucket(htab, b, hash, flags); + if (!ret) + ret =3D bpf_map_trace_update_elem(map, key, value, map_flags); return ret; } =20 @@ -1304,6 +1316,8 @@ static int __htab_lru_percpu_map_update_elem(struct b= pf_map *map, void *key, htab_unlock_bucket(htab, b, hash, flags); if (l_new) bpf_lru_push_free(&htab->lru, &l_new->lru_node); + if (!ret) + ret =3D bpf_map_trace_update_elem(map, key, value, map_flags); return ret; } =20 @@ -1354,6 +1368,8 @@ static int htab_map_delete_elem(struct bpf_map *map, = void *key) } =20 htab_unlock_bucket(htab, b, hash, flags); + if (!ret) + ret =3D bpf_map_trace_delete_elem(map, key); return ret; } =20 @@ -1390,6 +1406,8 @@ static int htab_lru_map_delete_elem(struct bpf_map *m= ap, void *key) htab_unlock_bucket(htab, b, hash, flags); if (l) htab_lru_push_free(htab, l); + if (!ret) + ret =3D bpf_map_trace_delete_elem(map, key); return ret; } =20 diff --git a/kernel/bpf/map_trace.c b/kernel/bpf/map_trace.c new file mode 100644 index 000000000000..336848e83daf --- /dev/null +++ b/kernel/bpf/map_trace.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2022 Google */ +#include "map_trace.h" + +noinline int bpf_map_trace_update_elem(struct bpf_map *map, void *key, + void *value, u64 map_flags) +{ + return 0; +} +ALLOW_ERROR_INJECTION(bpf_map_trace_update_elem, ERRNO); + +noinline int bpf_map_trace_delete_elem(struct bpf_map *map, void *key) +{ + return 0; +} +ALLOW_ERROR_INJECTION(bpf_map_trace_delete_elem, ERRNO); + diff --git a/kernel/bpf/map_trace.h b/kernel/bpf/map_trace.h new file mode 100644 index 000000000000..ae943af9e2a5 --- /dev/null +++ b/kernel/bpf/map_trace.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2022 Google */ +#ifndef __BPF_MAP_TRACE_H_ +#define __BPF_MAP_TRACE_H_ + +#include + +/* + * Map tracing hooks. They are called from some, but not all, bpf map type= s. + * For those map types which call them, the only guarantee is that they are + * called after the corresponding action (bpf_map_update_elem, etc.) takes + * effect. + */ +int bpf_map_trace_update_elem(struct bpf_map *map, void *key, + void *value, u64 map_flags); + +int bpf_map_trace_delete_elem(struct bpf_map *map, void *key); + +#endif // __BPF_MAP_TRACE_H_ --=20 2.34.1.448.ga2b2bfdf31-goog From nobody Wed Jul 1 01:55:01 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2E546C433EF for ; Wed, 5 Jan 2022 03:03:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237162AbiAEDD4 (ORCPT ); Tue, 4 Jan 2022 22:03:56 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56802 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235999AbiAEDDx (ORCPT ); Tue, 4 Jan 2022 22:03:53 -0500 Received: from mail-pg1-x52c.google.com (mail-pg1-x52c.google.com [IPv6:2607:f8b0:4864:20::52c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0B259C061785; Tue, 4 Jan 2022 19:03:53 -0800 (PST) Received: by mail-pg1-x52c.google.com with SMTP id r5so34329150pgi.6; Tue, 04 Jan 2022 19:03:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=YdqEdPYyId8S8WE+4fF4hSJObgeCrnl91l7Jm9e9Irc=; b=b0mBbZCabkSQl6rFSEXQhq6Lt8uVrpvWoDYZ96sODeaiuPmnpVF7loiYvsluLwtUiP o1MFlynhNDbm8LeMb1QtX24yftD8p4KGpn25vwkApTkQkDGq1YWACN9JHqJNaXYZlYmY qXe0OCwyWf00dgPGL4QaMGDddSmrShylD05zFeYGZSoOuHLXI0eB0/SsiwvYHc6PJtfr d9uCQS7Z4fNfxNEDTsaB6Bx3a+q1QqFLiK4FKaMywurPON+VI0/PTv1SqABNWYs/2ibH dKmH0Nl2YI87cmI8f8lIhFaHNYjwuuiP0z9J7kIIk/sn2wSv8AoMosETOAY3K51g0xbR bd/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=YdqEdPYyId8S8WE+4fF4hSJObgeCrnl91l7Jm9e9Irc=; b=vMN/uAAnW9SHMN9xADS0a78EmsL1uDQxl0KQJpD/vnWkS8rh9KAqe7Aos/Y2EU4QgK nDION8HB/lFOFnBtUIuVNbWtCySMi5i08qCyX3Ika/23nuABHj2/ikUCIs38q4d0BGrJ CsX8et9tknm1XNJTh22do41EoCLkZsocoeXRWAuHmF/dnY8wuUUalEaoM3USjZspTNyl +hsQQxzsZXZrJK7704ZH7ypEXAfYfpSrhPwjPq/50Koud6F2sPJjOb3wVm2aPVKKemOD orulhc50KkNW1A0IWsIjzrL24daqmU/KCoBXc14gwptr6A8s+GEph5bIY0kYEpijMHlr Cofg== X-Gm-Message-State: AOAM532uiP5gtn0GHGJGm1ilA3JOtzGHKjCL6bageknidbvc7cnviMxH EN0AExcBvcIV5oKePqQmrQ== X-Google-Smtp-Source: ABdhPJxY7QNwnsv8z2qW6FxY03NnBfljgrU5/2UedNDdWbCJfuWUp0Vr1hgjlwhHEq81PSjkQEwxTQ== X-Received: by 2002:a63:6845:: with SMTP id d66mr1517631pgc.199.1641351832567; Tue, 04 Jan 2022 19:03:52 -0800 (PST) Received: from jevburton2.c.googlers.com.com (157.214.185.35.bc.googleusercontent.com. [35.185.214.157]) by smtp.gmail.com with ESMTPSA id i9sm34280818pgc.27.2022.01.04.19.03.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Jan 2022 19:03:52 -0800 (PST) From: Joe Burton To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, ppenkov@google.com, sdf@google.com, haoluo@google.com Cc: Joe Burton Subject: [PATCH bpf-next v4 2/3] bpf: Add selftests Date: Wed, 5 Jan 2022 03:03:44 +0000 Message-Id: <20220105030345.3255846-3-jevburton.kernel@gmail.com> X-Mailer: git-send-email 2.34.1.448.ga2b2bfdf31-goog In-Reply-To: <20220105030345.3255846-1-jevburton.kernel@gmail.com> References: <20220105030345.3255846-1-jevburton.kernel@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Joe Burton Add selftests verifying that each supported map type is traced. Signed-off-by: Joe Burton --- .../selftests/bpf/prog_tests/map_trace.c | 171 ++++++++++++++++++ .../selftests/bpf/progs/bpf_map_trace.c | 95 ++++++++++ .../bpf/progs/bpf_map_trace_common.h | 12 ++ 3 files changed, 278 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/map_trace.c create mode 100644 tools/testing/selftests/bpf/progs/bpf_map_trace.c create mode 100644 tools/testing/selftests/bpf/progs/bpf_map_trace_common.h diff --git a/tools/testing/selftests/bpf/prog_tests/map_trace.c b/tools/tes= ting/selftests/bpf/prog_tests/map_trace.c new file mode 100644 index 000000000000..a622bbedd99d --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/map_trace.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Google */ +#include + +#include "bpf_map_trace.skel.h" +#include "progs/bpf_map_trace_common.h" + +#include +#include + +enum BoolOrErr { + TRUE =3D 0, + FALSE =3D 1, + ERROR =3D 2, +}; + +enum BoolOrErr percpu_key_is_set(struct bpf_map *map, uint32_t map_key) +{ + int num_cpus =3D libbpf_num_possible_cpus(); + uint64_t *percpu_map_val =3D NULL; + int map_fd =3D bpf_map__fd(map); + enum BoolOrErr ret =3D ERROR; + int err; + int i; + + if (!ASSERT_GE(num_cpus, 1, "get number of cpus")) + goto out; + + percpu_map_val =3D malloc(sizeof(*percpu_map_val) * num_cpus); + if (!ASSERT_NEQ(percpu_map_val, NULL, "allocate percpu map array")) + goto out; + + err =3D bpf_map_lookup_elem(map_fd, &map_key, percpu_map_val); + if (!ASSERT_EQ(err, 0, "map lookup update_elem")) + goto out; + + ret =3D FALSE; + for (i =3D 0; i < num_cpus; i++) + if (percpu_map_val[i] !=3D 0) + ret =3D TRUE; + +out: + if (percpu_map_val !=3D NULL) + free(percpu_map_val); + + return ret; +} + +enum BoolOrErr key_is_set(struct bpf_map *map, uint32_t map_key) +{ + int map_fd =3D bpf_map__fd(map); + uint32_t map_val; + int rc; + + rc =3D bpf_map_lookup_elem(map_fd, &map_key, &map_val); + if (!ASSERT_EQ(rc, 0, "array map lookup update_elem")) + return ERROR; + + return (map_val =3D=3D 0 ? FALSE : TRUE); +} + +void verify_map_contents(struct bpf_map_trace *skel) +{ + enum BoolOrErr rc_or_err; + struct bpf_map *map; + + map =3D skel->maps.array_map; + rc_or_err =3D key_is_set(map, ACCESS_LOC__TRACE_UPDATE); + if (!ASSERT_EQ(rc_or_err, TRUE, "array map updates are traced")) + return; + rc_or_err =3D key_is_set(map, ACCESS_LOC__TRACE_DELETE); + if (!ASSERT_EQ(rc_or_err, FALSE, "array map deletions are not traced")) + return; + + map =3D skel->maps.percpu_array_map; + rc_or_err =3D percpu_key_is_set(map, ACCESS_LOC__TRACE_UPDATE); + if (!ASSERT_EQ(rc_or_err, TRUE, "percpu array map updates are traced")) + return; + rc_or_err =3D percpu_key_is_set(map, ACCESS_LOC__TRACE_DELETE); + if (!ASSERT_EQ(rc_or_err, FALSE, + "percpu array map deletions are not traced")) + return; + + map =3D skel->maps.hash_map; + rc_or_err =3D key_is_set(map, ACCESS_LOC__TRACE_UPDATE); + if (!ASSERT_EQ(rc_or_err, TRUE, "hash map updates are traced")) + return; + rc_or_err =3D key_is_set(map, ACCESS_LOC__TRACE_DELETE); + if (!ASSERT_EQ(rc_or_err, TRUE, "hash map deletions are traced")) + return; + + map =3D skel->maps.percpu_hash_map; + rc_or_err =3D percpu_key_is_set(map, ACCESS_LOC__TRACE_UPDATE); + if (!ASSERT_EQ(rc_or_err, TRUE, "percpu hash map updates are traced")) + return; + rc_or_err =3D percpu_key_is_set(map, ACCESS_LOC__TRACE_DELETE); + if (!ASSERT_EQ(rc_or_err, TRUE, + "percpu hash map deletions are traced")) + return; + + map =3D skel->maps.lru_hash_map; + rc_or_err =3D key_is_set(map, ACCESS_LOC__TRACE_UPDATE); + if (!ASSERT_EQ(rc_or_err, TRUE, "lru_hash map updates are traced")) + return; + rc_or_err =3D key_is_set(map, ACCESS_LOC__TRACE_DELETE); + if (!ASSERT_EQ(rc_or_err, TRUE, "lru_hash map deletions are traced")) + return; + + map =3D skel->maps.percpu_lru_hash_map; + rc_or_err =3D percpu_key_is_set(map, ACCESS_LOC__TRACE_UPDATE); + if (!ASSERT_EQ(rc_or_err, TRUE, + "percpu lru hash map updates are traced")) + return; + rc_or_err =3D percpu_key_is_set(map, ACCESS_LOC__TRACE_DELETE); + if (!ASSERT_EQ(rc_or_err, TRUE, + "percpu lru hash map deletions are traced")) + return; +} + +void map_trace_test(void) +{ + struct bpf_map_trace *skel; + ssize_t bytes_written; + char write_buf =3D 'a'; + int write_fd =3D -1; + int rc; + + /* + * Load and attach programs. + */ + skel =3D bpf_map_trace__open_and_load(); + if (!ASSERT_NEQ(skel, NULL, "open/load skeleton")) + return; + + rc =3D bpf_map_trace__attach(skel); + if (!ASSERT_EQ(rc, 0, "attach skeleton")) + goto out; + + /* + * Invoke core BPF program. + */ + write_fd =3D open("/tmp/map_trace_test_file", O_CREAT | O_WRONLY); + if (!ASSERT_GE(rc, 0, "open tmp file for writing")) + goto out; + + bytes_written =3D write(write_fd, &write_buf, sizeof(write_buf)); + if (!ASSERT_EQ(bytes_written, sizeof(write_buf), "write to tmp file")) + return; + + /* + * Verify that tracing programs were invoked as expected. + */ + verify_map_contents(skel); + +out: + if (skel) + bpf_map_trace__destroy(skel); + if (write_fd !=3D -1) + close(write_fd); +} + +void test_map_trace(void) +{ + /* + * Trampoline programs are only supported on x86. + */ +#if defined(__x86_64__) + map_trace_test(); +#endif +} + diff --git a/tools/testing/selftests/bpf/progs/bpf_map_trace.c b/tools/test= ing/selftests/bpf/progs/bpf_map_trace.c new file mode 100644 index 000000000000..90d4d435fe59 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_map_trace.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Google */ +#include "vmlinux.h" + +#include +#include +#include +#include + +#include "bpf_map_trace_common.h" + +#define DECLARE_MAP(name, map_type) \ + struct { \ + __uint(type, map_type); \ + __uint(max_entries, __ACCESS_LOC__MAX); \ + __type(key, u32); \ + __type(value, u32); \ + } name SEC(".maps") + +DECLARE_MAP(array_map, BPF_MAP_TYPE_ARRAY); +DECLARE_MAP(percpu_array_map, BPF_MAP_TYPE_PERCPU_ARRAY); +DECLARE_MAP(hash_map, BPF_MAP_TYPE_HASH); +DECLARE_MAP(percpu_hash_map, BPF_MAP_TYPE_PERCPU_HASH); +DECLARE_MAP(lru_hash_map, BPF_MAP_TYPE_LRU_HASH); +DECLARE_MAP(percpu_lru_hash_map, BPF_MAP_TYPE_LRU_PERCPU_HASH); + +static inline void __log_location(void *map, + enum MapAccessLocations location) +{ + u32 key =3D location; + u32 val =3D 1; + + bpf_map_update_elem(map, &key, &val, /*flags=3D*/0); +} + +static inline void log_location(struct bpf_map *map, + enum MapAccessLocations location) +{ + if (map =3D=3D &array_map) + __log_location(&array_map, location); + if (map =3D=3D &percpu_array_map) + __log_location(&percpu_array_map, location); + if (map =3D=3D &hash_map) + __log_location(&hash_map, location); + if (map =3D=3D &percpu_hash_map) + __log_location(&percpu_hash_map, location); + if (map =3D=3D &lru_hash_map) + __log_location(&lru_hash_map, location); + if (map =3D=3D &percpu_lru_hash_map) + __log_location(&percpu_lru_hash_map, location); +} + +SEC("fentry/bpf_map_trace_update_elem") +int BPF_PROG(fentry__bpf_map_trace_update_elem, + struct bpf_map *map, void *key, + void *value, u64 map_flags) +{ + log_location(map, ACCESS_LOC__TRACE_UPDATE); + return 0; +} + +SEC("fentry/bpf_map_trace_delete_elem") +int BPF_PROG(fentry__bpf_map_trace_delete_elem, + struct bpf_map *map, void *key) +{ + log_location(map, ACCESS_LOC__TRACE_DELETE); + return 0; +} + +static inline void do_map_accesses(void *map) +{ + u32 key =3D ACCESS_LOC__APP; + u32 val =3D 1; + + bpf_map_update_elem(map, &key, &val, /*flags=3D*/0); + bpf_map_delete_elem(map, &key); +} + +SEC("fentry/__x64_sys_write") +int BPF_PROG(fentry__x64_sys_write, struct pt_regs *regs, int ret) +{ + /* + * Trigger an update and a delete for every map type under test. + * We want to verify that bpf_map_trace_{update,delete}_elem() fire + * for each map type. + */ + do_map_accesses(&array_map); + do_map_accesses(&percpu_array_map); + do_map_accesses(&hash_map); + do_map_accesses(&percpu_hash_map); + do_map_accesses(&lru_hash_map); + do_map_accesses(&percpu_lru_hash_map); + return 0; +} + diff --git a/tools/testing/selftests/bpf/progs/bpf_map_trace_common.h b/too= ls/testing/selftests/bpf/progs/bpf_map_trace_common.h new file mode 100644 index 000000000000..8986e6286350 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_map_trace_common.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2022 Google */ +#pragma once + +enum MapAccessLocations { + ACCESS_LOC__APP, + ACCESS_LOC__TRACE_UPDATE, + ACCESS_LOC__TRACE_DELETE, + + __ACCESS_LOC__MAX, +}; + --=20 2.34.1.448.ga2b2bfdf31-goog From nobody Wed Jul 1 01:55:01 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 22F62C4332F for ; Wed, 5 Jan 2022 03:04:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237169AbiAEDEA (ORCPT ); Tue, 4 Jan 2022 22:04:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56810 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237142AbiAEDDy (ORCPT ); Tue, 4 Jan 2022 22:03:54 -0500 Received: from mail-pg1-x535.google.com (mail-pg1-x535.google.com [IPv6:2607:f8b0:4864:20::535]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D1D55C061761; Tue, 4 Jan 2022 19:03:53 -0800 (PST) Received: by mail-pg1-x535.google.com with SMTP id t32so2748463pgm.7; Tue, 04 Jan 2022 19:03:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=JObbU+jx5qBItDPjFj/+xryJ7de4qU7LJo9+az2AAoY=; b=DoWX2NOUwjfg5xTMeEOqIkmIH3gWM4yFCbQViaHJjtrOtP+80uv8bn/A92qEgvq7S+ tiIJzC8vKKv4Vg4sTHL8S5bVtvwKtAk6GHEE4f+/SPlE9m4qh7y71yrU3SMxeb6dgGW5 JwiwBDItvcuVrSko1ZlUhXgxHW7QPi5MOJ/ATzaqmCAFURP5OyejFsWj+VGcmWlrxdhw Y9F0o2OKwh9bnDFdCs/vvQX01Znau43965BcoUyNVrtqlJhM2oEae6M7sAl+DDRCB5bx zUTYgPMI8T4amKOYJgjeyOurJjqKBcSHpZ7RTeYJ3m59l9FUbrwhdS9EaN43vrKSM8sC 5Jxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=JObbU+jx5qBItDPjFj/+xryJ7de4qU7LJo9+az2AAoY=; b=kRR0sOmr9sqFslJBUqhPKsYX2O/pOaxJtSIH4CY/e7Qd69biRcAt/owi4unNkAlSR/ yK7n0zd8ZjgYz5QmthaofzCYZYbT8K7tHBRUIHhUPH5QK532hGe2C50GaN2eaxIn4qI2 RIfnZ9dOXFpjqv0uFJFpi9NrvrkiGrLvjFljalJ4fSyaI3n+wQFFuwoLf4Jfi8pyImha xAqIcRq8quWWbKIc1kROkS3wTcpDMEnPdiqn1Bz0Juw90N8IRpHQ+6vYrpx66Y9p49r3 dopyexbiWx/HemKG++WqWyeAnUJjNWOYwY1WXORdswef+Rg+ugdq9DLjhAKiEyjUmbOO UXhw== X-Gm-Message-State: AOAM53274dxNf2Uvb9d0KR5Vc/bH8J3CaTuNOQngsV0X119d7sMGdFqF 4aA1QDUzWO3OuFYleWMpBw== X-Google-Smtp-Source: ABdhPJwhjFQ4iuYlMOKiozF2IHC0gJTDT3/TESWvcAm8s4SlRjXYjQKLKu4mcs8BcPX4gZPIKQU4rw== X-Received: by 2002:a63:951b:: with SMTP id p27mr46987430pgd.524.1641351833042; Tue, 04 Jan 2022 19:03:53 -0800 (PST) Received: from jevburton2.c.googlers.com.com (157.214.185.35.bc.googleusercontent.com. [35.185.214.157]) by smtp.gmail.com with ESMTPSA id i9sm34280818pgc.27.2022.01.04.19.03.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 04 Jan 2022 19:03:52 -0800 (PST) From: Joe Burton To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , John Fastabend , KP Singh , linux-kernel@vger.kernel.org, netdev@vger.kernel.org, bpf@vger.kernel.org, ppenkov@google.com, sdf@google.com, haoluo@google.com Cc: Joe Burton Subject: [PATCH bpf-next v4 3/3] bpf: Add real world example for map tracing Date: Wed, 5 Jan 2022 03:03:45 +0000 Message-Id: <20220105030345.3255846-4-jevburton.kernel@gmail.com> X-Mailer: git-send-email 2.34.1.448.ga2b2bfdf31-goog In-Reply-To: <20220105030345.3255846-1-jevburton.kernel@gmail.com> References: <20220105030345.3255846-1-jevburton.kernel@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Joe Burton Add an example that demonstrates how map tracing helps us avoid race conditions while upgrading stateful programs. Signed-off-by: Joe Burton --- .../selftests/bpf/prog_tests/map_trace.c | 258 +++++++++++++++++- .../progs/bpf_map_trace_real_world_common.h | 125 +++++++++ .../bpf_map_trace_real_world_migration.c | 102 +++++++ .../bpf/progs/bpf_map_trace_real_world_new.c | 4 + .../bpf/progs/bpf_map_trace_real_world_old.c | 5 + 5 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/progs/bpf_map_trace_real_wo= rld_common.h create mode 100644 tools/testing/selftests/bpf/progs/bpf_map_trace_real_wo= rld_migration.c create mode 100644 tools/testing/selftests/bpf/progs/bpf_map_trace_real_wo= rld_new.c create mode 100644 tools/testing/selftests/bpf/progs/bpf_map_trace_real_wo= rld_old.c diff --git a/tools/testing/selftests/bpf/prog_tests/map_trace.c b/tools/tes= ting/selftests/bpf/prog_tests/map_trace.c index a622bbedd99d..00648d44b2b6 100644 --- a/tools/testing/selftests/bpf/prog_tests/map_trace.c +++ b/tools/testing/selftests/bpf/prog_tests/map_trace.c @@ -3,6 +3,9 @@ #include =20 #include "bpf_map_trace.skel.h" +#include "bpf_map_trace_real_world_migration.skel.h" +#include "bpf_map_trace_real_world_new.skel.h" +#include "bpf_map_trace_real_world_old.skel.h" #include "progs/bpf_map_trace_common.h" =20 #include @@ -139,7 +142,7 @@ void map_trace_test(void) /* * Invoke core BPF program. */ - write_fd =3D open("/tmp/map_trace_test_file", O_CREAT | O_WRONLY); + write_fd =3D open("/dev/null", O_CREAT | O_WRONLY); if (!ASSERT_GE(rc, 0, "open tmp file for writing")) goto out; =20 @@ -159,6 +162,258 @@ void map_trace_test(void) close(write_fd); } =20 +int real_world_example__attach_migration( + struct bpf_map_trace_real_world_migration *migration_skel, + struct bpf_link **iter_link, + struct bpf_link **map_trace_link_update, + struct bpf_link **map_trace_link_delete) +{ + union bpf_iter_link_info iter_link_info; + struct bpf_iter_attach_opts iter_opts; + int64_t error; + + *map_trace_link_update =3D bpf_program__attach( + migration_skel->progs.copy_on_write__update); + error =3D libbpf_get_error(map_trace_link_update); + if (!ASSERT_EQ(error, 0, + "copy_on_write update bpf_program__attach failure")) + return 1; + + *map_trace_link_delete =3D bpf_program__attach( + migration_skel->progs.copy_on_write__delete); + error =3D libbpf_get_error(map_trace_link_delete); + if (!ASSERT_EQ(error, 0, + "copy_on_write update bpf_program__delete failure")) + return 1; + + memset(&iter_link_info, 0, sizeof(iter_link_info)); + iter_link_info.map.map_fd =3D bpf_map__fd(migration_skel->maps.old_map); + + memset(&iter_opts, 0, sizeof(iter_opts)); + iter_opts.sz =3D sizeof(iter_opts); + iter_opts.link_info =3D &iter_link_info; + iter_opts.link_info_len =3D sizeof(iter_link_info); + *iter_link =3D bpf_program__attach_iter( + migration_skel->progs.bulk_migration, &iter_opts); + error =3D libbpf_get_error(iter_link); + if (!ASSERT_EQ(error, 0, "bpf_program__attach_iter failure")) + return 1; + + return 0; +} + +int open_and_write_files(const char *path, size_t num_files) +{ + int *fds =3D malloc(sizeof(int) * num_files); + ssize_t bytes_written; + const char buf =3D 'a'; + size_t i, j; + int ret =3D 0; + + if (fds =3D=3D NULL) + return 1; + + for (i =3D 0; i < num_files; i++) { + fds[i] =3D open(path, O_WRONLY | O_CREAT); + + if (fds[i] < 0) { + ret =3D 2; + break; + } + bytes_written =3D write(fds[i], &buf, sizeof(buf)); + if (bytes_written !=3D sizeof(buf)) { + ret =3D 3; + break; + } + } + for (j =3D 0; j < i; j++) + close(fds[j]); + return ret; +} + +void real_world_example(void) +{ + struct bpf_map_trace_real_world_migration *migration_skel =3D NULL; + int file_fd_should_write =3D -1, file_fd_should_not_write =3D -1; + struct bpf_map_trace_real_world_new *new_skel =3D NULL; + struct bpf_map_trace_real_world_old *old_skel =3D NULL; + struct bpf_link *map_trace_link_update =3D NULL; + struct bpf_link *map_trace_link_delete =3D NULL; + struct bpf_link *iter_link =3D NULL; + const bool enable_filtering =3D 1; + const uint32_t pid =3D getpid(); + uint32_t max_open_files; + char file_buf =3D 'a'; + int iter_fd =3D -1; + char iter_buf[1]; + int rc; + + /* + * Begin by loading and attaching the old version of our program. + */ + old_skel =3D bpf_map_trace_real_world_old__open_and_load(); + if (!ASSERT_NEQ(old_skel, NULL, "open/load old skeleton")) + return; + rc =3D bpf_map_trace_real_world_old__attach(old_skel); + if (!ASSERT_EQ(rc, 0, "attach old skeleton")) { + fprintf(stderr, "Failed to attach skeleton: %d\n", errno); + goto out; + } + rc =3D bpf_map_update_elem(bpf_map__fd(old_skel->maps.filtered_pids), + &pid, &enable_filtering, /*flags=3D*/0); + if (!ASSERT_EQ(rc, 0, "configure process to be filtered")) + return; + if (!ASSERT_EQ(open_and_write_files("/tmp/tst_file", 1), 0, + "program allows writing a single new file")) + goto out; + max_open_files =3D bpf_map__max_entries(old_skel->maps.allow_reads); + if (!ASSERT_NEQ(open_and_write_files("/tmp/tst_file", + max_open_files + 1), 0, + "program blocks writing too many new files")) + goto out; + + /* + * Then load the new version of the program. + */ + new_skel =3D bpf_map_trace_real_world_new__open_and_load(); + if (!ASSERT_NEQ(new_skel, NULL, "open/load new skeleton")) + goto out; + + /* + * Hook up the migration programs. This gives the old map + * copy-on-write semantics. + */ + migration_skel =3D bpf_map_trace_real_world_migration__open(); + if (!ASSERT_NEQ(migration_skel, NULL, "open migration skeleton")) + goto out; + rc =3D bpf_map__reuse_fd(migration_skel->maps.old_map, + bpf_map__fd(old_skel->maps.allow_reads)); + if (!ASSERT_EQ(rc, 0, "reuse old map fd")) + goto out; + rc =3D bpf_map__reuse_fd(migration_skel->maps.new_map, + bpf_map__fd(new_skel->maps.allow_reads)); + if (!ASSERT_EQ(rc, 0, "reuse new map fd")) + goto out; + rc =3D bpf_map_trace_real_world_migration__load(migration_skel); + if (!ASSERT_EQ(rc, 0, "load migration skeleton")) + goto out; + rc =3D real_world_example__attach_migration(migration_skel, + &iter_link, + &map_trace_link_update, + &map_trace_link_delete); + if (!ASSERT_EQ(rc, 0, "attach migration programs")) + goto out; + + /* + * Simulated race condition type 1: An application opens an fd before + * bulk transfer and closes it after. + */ + file_fd_should_not_write =3D open("/tmp/tst_file", O_WRONLY | O_CREAT); + if (!ASSERT_GE(file_fd_should_not_write, 0, + "open file before bulk migration")) + goto out; + + /* + * Perform bulk transfer. + */ + iter_fd =3D bpf_iter_create(bpf_link__fd(iter_link)); + if (!ASSERT_GE(iter_fd, 0, "create iterator")) + goto out; + rc =3D read(iter_fd, &iter_buf, sizeof(iter_buf)); + if (!ASSERT_EQ(rc, 0, "execute map iterator")) + goto out; + rc =3D bpf_map_update_elem(bpf_map__fd(new_skel->maps.filtered_pids), + &pid, &enable_filtering, /*flags=3D*/0); + if (!ASSERT_EQ(rc, 0, "configure process to be filtered")) + goto out; + + /* + * Simulated race condition type 1 (continued). This close() does not + * propagate to the new map without copy-on-write semantics, so it + * would occupy a spot in the map until our app happens to close an fd + * with the same number. This would subtly degrade the contract with + * the application. + */ + close(file_fd_should_not_write); + file_fd_should_not_write =3D -1; + + /* + * Simulated race condition type 2: An application opens a file + * descriptor after bulk transfer. This openat() does not propagate to + * the new map without copy-on-write, so our app would not be able to + * write to it. + */ + file_fd_should_write =3D open("/tmp/tst_file", O_WRONLY | O_CREAT); + if (!ASSERT_GE(file_fd_should_write, 0, + "open file after bulk migration")) + goto out; + + /* + * State is migrated. Load new programs. + */ + rc =3D bpf_map_trace_real_world_new__attach(new_skel); + if (!ASSERT_EQ(rc, 0, "failed to attach new programs")) + goto out; + + /* + * Unload migration progs. + */ + close(iter_fd); + iter_fd =3D -1; + bpf_link__destroy(map_trace_link_update); + map_trace_link_update =3D NULL; + bpf_link__destroy(map_trace_link_delete); + map_trace_link_delete =3D NULL; + bpf_link__destroy(iter_link); + iter_link =3D NULL; + bpf_map_trace_real_world_migration__destroy(migration_skel); + migration_skel =3D NULL; + + /* + * Unload old programs. + */ + bpf_map_trace_real_world_old__destroy(old_skel); + old_skel =3D NULL; + + if (!ASSERT_EQ(open_and_write_files("/tmp/tst_file", 1), 0, + "program allows writing a single new file")) + goto out; + max_open_files =3D bpf_map__max_entries(new_skel->maps.allow_reads); + if (!ASSERT_NEQ(open_and_write_files("/tmp/tst_file", + max_open_files + 1), 0, + "program blocks writing too many new files")) + goto out; + /* + * Simulated race condition type 2 (continued): If we didn't do + * copy-on-write, this would be expected to fail, since the FD would + * not be in the new map. + */ + rc =3D write(file_fd_should_write, &file_buf, sizeof(file_buf)); + if (!ASSERT_EQ(rc, sizeof(file_buf), + "migrated program allows writing to file opened before migration"= )) + goto out; + +out: + if (old_skel) + bpf_map_trace_real_world_old__destroy(old_skel); + if (new_skel) + bpf_map_trace_real_world_new__destroy(new_skel); + if (migration_skel) + bpf_map_trace_real_world_migration__destroy(migration_skel); + if (map_trace_link_update) + bpf_link__destroy(map_trace_link_update); + if (map_trace_link_delete) + bpf_link__destroy(map_trace_link_delete); + if (iter_link) + bpf_link__destroy(iter_link); + if (iter_fd > -1) + close(iter_fd); + if (file_fd_should_write > -1) + close(file_fd_should_write); + if (file_fd_should_not_write > -1) + close(file_fd_should_not_write); +} + void test_map_trace(void) { /* @@ -166,6 +421,7 @@ void test_map_trace(void) */ #if defined(__x86_64__) map_trace_test(); + real_world_example(); #endif } =20 diff --git a/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_com= mon.h b/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_common.h new file mode 100644 index 000000000000..0311f1d4e99f --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_common.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2022 Google */ +#pragma once + +#include "vmlinux.h" + +#include +#include +#include +#include + +/* + * Mock "real world" application. + * + * Blocks all writes from a set of applications. A limited number of newly + * openat()ed file descriptors file descriptors may be written to. Writes = to + * already-open file descriptors are blocked. + * + * The affected processes are selected by populating filtered_pid. + * + * It is intended as an example of a stateful policy-enforcement applicati= on + * which benefits from map tracing. It is not intended to be useful. + */ + +/* + * This is the only difference between the old and new application. Since = we're + * enforcing a policy based on this data, we want to migrate it. Since the + * application can modify the data in parallel, we need to give this map + * copy-on-write semantics so that those changes propagate. + */ +#if defined(OLD_VERSION) +struct allow_reads_key { + uint32_t pid; + int fd; +}; +#else +struct allow_reads_key { + int fd; + uint32_t pid; +}; +#endif +struct allow_reads_value { + bool do_allow; +}; +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 16); + __type(key, struct allow_reads_key); + __type(value, struct allow_reads_value); +} allow_reads SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 16); + __type(key, uint32_t); + __type(value, bool); +} filtered_pids SEC(".maps"); + + +SEC("kretprobe/__x64_sys_openat") +int BPF_KRETPROBE(kretprobe__x64_sys_openat, int ret) +{ + struct allow_reads_key key; + struct allow_reads_value val; + uint32_t pid; + char *pid_is_filtered; + + pid =3D (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF; + memset(&key, 0, sizeof(key)); + key.pid =3D pid; + key.fd =3D ret; + val.do_allow =3D true; + + if (ret < 0) + return 0; + + pid_is_filtered =3D bpf_map_lookup_elem(&filtered_pids, &pid); + if (!pid_is_filtered) + return 0; + + if (!*pid_is_filtered) + return 0; + + /* + * Ignore errors. Failing to insert has the effect of blocking writes + * on that file descriptor. + */ + bpf_map_update_elem(&allow_reads, &key, &val, /*flags=3D*/0); + return 0; +} + +SEC("fmod_ret/__x64_sys_write") +int BPF_PROG(fmod_ret__x64_sys_write, struct pt_regs *regs, int ret) +{ + int fd =3D PT_REGS_PARM1(regs); + struct allow_reads_value *val; + struct allow_reads_key key; + + memset(&key, 0, sizeof(key)); + key.pid =3D (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF; + key.fd =3D fd; + val =3D bpf_map_lookup_elem(&allow_reads, &key); + if (!val) + return -EPERM; + return val->do_allow ? 0 : -EPERM; +} + +SEC("fmod_ret/__x64_sys_close") +int BPF_PROG(fmod_ret__x64_sys_close, struct pt_regs *regs, int ret) +{ + int fd =3D PT_REGS_PARM1(regs); + struct allow_reads_key key; + struct allow_reads_value val; + + memset(&key, 0, sizeof(key)); + key.pid =3D (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF; + key.fd =3D fd; + val.do_allow =3D true; + + bpf_map_delete_elem(&allow_reads, &key); + return 0; +} + +char _license[] SEC("license") =3D "GPL"; + diff --git a/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_mig= ration.c b/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_migra= tion.c new file mode 100644 index 000000000000..842dc4bc382a --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_migration.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Google */ +#include "vmlinux.h" + +#include +#include + +/* In the "real" real world, we would use BTF to generate a program which = knows + * about the old and new map ABI. To keep things simple we'll just use a + * statically defined program which knows about them. + */ +struct allow_reads_key__old { + uint32_t pid; + int fd; +}; +struct allow_reads_key__new { + int fd; + uint32_t pid; +}; +struct allow_reads_value__old { + bool do_drop; +}; +struct allow_reads_value__new { + bool do_drop; +}; + +/* Likewise, in the "real" real world we would simply generate a program + * containing the fd of this map. For libbpf to generate a skeleton for us= we + * need to dupicate this definition. + */ +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 100); + __type(key, struct allow_reads_key__old); + __type(value, struct allow_reads_value__old); +} old_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(max_entries, 100); + __type(key, struct allow_reads_key__new); + __type(value, struct allow_reads_value__new); +} new_map SEC(".maps"); + +static inline void read_migrate_write(void *key, void *value) +{ + struct allow_reads_key__old old_key =3D {}; + struct allow_reads_key__new new_key =3D {}; + char old_value =3D 0; + + if (bpf_probe_read_kernel(&old_key, sizeof(old_key), key)) + return; + if (bpf_probe_read_kernel(&old_value, sizeof(old_value), value)) + return; + + new_key.pid =3D old_key.pid; + new_key.fd =3D old_key.fd; + + bpf_map_update_elem(&new_map, &new_key, &old_value, /*flags=3D*/0); +} + +SEC("fentry/bpf_map_trace_update_elem") +int BPF_PROG(copy_on_write__update, + struct bpf_map *map, void *key, + void *value, u64 map_flags) +{ + if (map =3D=3D &old_map) + read_migrate_write(key, value); + return 0; +} + +static inline void read_migrate_delete(void *key) +{ + struct allow_reads_key__old old_key =3D {}; + struct allow_reads_key__new new_key =3D {}; + + if (bpf_probe_read(&old_key, sizeof(old_key), key)) + return; /* Could write to a map here */ + + new_key.pid =3D old_key.pid; + new_key.fd =3D old_key.fd; + + bpf_map_delete_elem(&new_map, &new_key); +} + +SEC("fentry/bpf_map_trace_delete_elem") +int BPF_PROG(copy_on_write__delete, + struct bpf_map *map, void *key) +{ + if (map =3D=3D &old_map) + read_migrate_delete(key); + return 0; +} + +SEC("iter/bpf_map_elem") +int bulk_migration(struct bpf_iter__bpf_map_elem *ctx) +{ + read_migrate_write(ctx->key, ctx->value); + return 0; +} + +char _license[] SEC("license") =3D "GPL"; + diff --git a/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_new= .c b/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_new.c new file mode 100644 index 000000000000..4291a39c8009 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_new.c @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Google */ +#include "bpf_map_trace_real_world_common.h" + diff --git a/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_old= .c b/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_old.c new file mode 100644 index 000000000000..4124d4c96d55 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_old.c @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Google */ +#define OLD_VERSION +#include "bpf_map_trace_real_world_common.h" + --=20 2.34.1.448.ga2b2bfdf31-goog