From patchwork Tue Nov 2 02:14:30 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Burton X-Patchwork-Id: 12597749 X-Patchwork-Delegate: bpf@iogearbox.net 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 mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BFCC1C4332F for ; Tue, 2 Nov 2021 02:14:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A527760EE3 for ; Tue, 2 Nov 2021 02:14:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229931AbhKBCR0 (ORCPT ); Mon, 1 Nov 2021 22:17:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46044 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229616AbhKBCRX (ORCPT ); Mon, 1 Nov 2021 22:17:23 -0400 Received: from mail-pl1-x62c.google.com (mail-pl1-x62c.google.com [IPv6:2607:f8b0:4864:20::62c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9EBFBC061714; Mon, 1 Nov 2021 19:14:49 -0700 (PDT) Received: by mail-pl1-x62c.google.com with SMTP id y1so13600269plk.10; Mon, 01 Nov 2021 19:14:49 -0700 (PDT) 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=n3iM6toE0BIl0nwK2d7kUxN8Y6KLsF/QyO7hct74M6U=; b=atU253/wEHO8xkOZY24KbBAIQULd9LCYfAK+qerzfNh2DsRAuFrZxs/O0M5PHwpX2K yITcx1Eyt/NfvG5RG/u1Ww+cwBrD614DrZfpjLymTpdXRdeL+Y6qAwk4gQAxXcv+fH8f p7e28TH0hmWouRzPgheXj7zsprcuzH+1JH7AOEBlPDNBCGnq3ZTWwmVawxaxkM0SgLDE fJhswN1lNV/7LY6tXEw/7hWw55ejGKLHCkr5hRa9F7S4BErJhb90Yv4s9EtrDOTE7ftv nfsmJcvezWmLFgkmijBE7Az76Wn9CHKjPLr46pM3Vc0EgMHV16aGrLO5NO77sS/7+yCI 9Lrw== 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=n3iM6toE0BIl0nwK2d7kUxN8Y6KLsF/QyO7hct74M6U=; b=YyB0ugnuLvoUw/g8ISDHu+8JTeAY4htMWL+qqYpGzmy0OIejgjXKm1CdEEarzv8CyG Tb/pCoGxU5kWuD9KLye7kJus0C7e+xO39s1G6Bp1oz0pHV2X84Oc6cAgjkugsIinwpC+ soQDD5G1ySNS2RWS0UGrAfJ3cZ3Y+N+XlnrYYI9ACdiGOlwkun3W3VH7ftZEOQkTdorW JUBRDYL7E6iovfZ7qq+gFNP+qinQHl86xsRnMo6b4u3V0U7bMAXSEjO1iaQTDhV+i7cq vYHd9+QLSiy2JcRyMEAxwGHl2SXhYZS1nJ6wVWahZVFTdbFS4hQdr7ggFHKAAwKTvTzv Nffw== X-Gm-Message-State: AOAM530tVjq6FAX1EkAmkDmsLwnURBDvXHG4FFbxmgUH4czqSTI9Cq/8 4EacGr8kE/USDlPYakly9Q== X-Google-Smtp-Source: ABdhPJwlveRQxGPLKnmoR1IUUGZL85BZnzJHKEFW5pczjqb+o9aQm5H6GT4lyicqIdjgRVHw/7mooA== X-Received: by 2002:a17:903:30cd:b0:141:c6dd:4d03 with SMTP id s13-20020a17090330cd00b00141c6dd4d03mr16385969plc.16.1635819289206; Mon, 01 Nov 2021 19:14:49 -0700 (PDT) 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 j6sm14051446pgf.60.2021.11.01.19.14.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Nov 2021 19:14:48 -0700 (PDT) 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 Cc: Petar Penkov , Stanislav Fomichev , Joe Burton Subject: [RFC PATCH v3 1/3] bpf: Add map tracing functions and call sites Date: Tue, 2 Nov 2021 02:14:30 +0000 Message-Id: <20211102021432.2807760-2-jevburton.kernel@gmail.com> X-Mailer: git-send-email 2.33.1.1089.g2158813163f-goog In-Reply-To: <20211102021432.2807760-1-jevburton.kernel@gmail.com> References: <20211102021432.2807760-1-jevburton.kernel@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC 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 | 6 ++++++ kernel/bpf/hashtab.c | 25 +++++++++++++++++++++++++ kernel/bpf/map_trace.c | 25 +++++++++++++++++++++++++ kernel/bpf/map_trace.h | 18 ++++++++++++++++++ 5 files changed, 75 insertions(+), 1 deletion(-) 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 cf6ca339f3cd..03ab5c058e73 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -9,7 +9,7 @@ CFLAGS_core.o += $(call cc-disable-warning, override-init) $(cflags-nogcse-yy) obj-$(CONFIG_BPF_SYSCALL) += 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) += 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) += local_storage.o queue_stack_maps.o ringbuf.o -obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o +obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o map_trace.o obj-${CONFIG_BPF_LSM} += bpf_inode_storage.o obj-$(CONFIG_BPF_SYSCALL) += disasm.o obj-$(CONFIG_BPF_JIT) += trampoline.o diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 5e1ccfae916b..a0b4f1769e17 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -13,6 +13,7 @@ #include #include "map_in_map.h" +#include "map_trace.h" #define ARRAY_CREATE_FLAG_MASK \ (BPF_F_NUMA_NODE | BPF_F_MMAPABLE | BPF_F_ACCESS_MASK | \ @@ -300,6 +301,7 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value, struct bpf_array *array = container_of(map, struct bpf_array, map); u32 index = *(u32 *)key; char *val; + int err; if (unlikely((map_flags & ~BPF_F_LOCK) > BPF_EXIST)) /* unknown flags */ @@ -317,6 +319,10 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value, !map_value_has_spin_lock(map))) return -EINVAL; + err = bpf_map_trace_update_elem(map, key, value, map_flags); + if (unlikely(err)) + return err; + if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { memcpy(this_cpu_ptr(array->pptrs[index & array->index_mask]), value, map->value_size); diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index d29af9988f37..c1816a615d82 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" #define HTAB_CREATE_FLAG_MASK \ (BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE | \ @@ -1041,6 +1042,10 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value, b = __select_bucket(htab, hash); head = &b->head; + ret = bpf_map_trace_update_elem(map, key, value, map_flags); + if (unlikely(ret)) + return ret; + if (unlikely(map_flags & BPF_F_LOCK)) { if (unlikely(!map_value_has_spin_lock(map))) return -EINVAL; @@ -1133,6 +1138,10 @@ static int htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value, /* unknown flags */ return -EINVAL; + ret = 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()); @@ -1201,6 +1210,10 @@ static int __htab_percpu_map_update_elem(struct bpf_map *map, void *key, /* unknown flags */ return -EINVAL; + ret = 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()); @@ -1256,6 +1269,10 @@ static int __htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key, /* unknown flags */ return -EINVAL; + ret = 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()); @@ -1334,6 +1351,10 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key) WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && !rcu_read_lock_bh_held()); + ret = bpf_map_trace_delete_elem(map, key); + if (unlikely(ret)) + return ret; + key_size = map->key_size; hash = htab_map_hash(key, key_size, htab->hashrnd); @@ -1370,6 +1391,10 @@ static int htab_lru_map_delete_elem(struct bpf_map *map, void *key) WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && !rcu_read_lock_bh_held()); + ret = bpf_map_trace_delete_elem(map, key); + if (unlikely(ret)) + return ret; + key_size = map->key_size; hash = htab_map_hash(key, key_size, htab->hashrnd); diff --git a/kernel/bpf/map_trace.c b/kernel/bpf/map_trace.c new file mode 100644 index 000000000000..661b433f1451 --- /dev/null +++ b/kernel/bpf/map_trace.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2021 Google */ +#include "map_trace.h" + +noinline int bpf_map_trace_update_elem(struct bpf_map *map, void *key, + void *value, u64 map_flags) +{ + /* + * Noop side effect prevents call site from being optimized out. + */ + asm(""); + return 0; +} +ALLOW_ERROR_INJECTION(bpf_map_trace_update_elem, ERRNO); + +noinline int bpf_map_trace_delete_elem(struct bpf_map *map, void *key) +{ + /* + * Noop side effect prevents call site from being optimized out. + */ + asm(""); + 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..12356a2e1f9f --- /dev/null +++ b/kernel/bpf/map_trace.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2021 Google */ +#pragma once + +#include + +/* + * Map tracing hooks. They are called from some, but not all, bpf map types. + * For those map types which call them, the only guarantee is that they are + * called before the corresponding action (bpf_map_update_elem, etc.) takes + * effect. Thus an fmod_ret program may use these hooks to prevent a map from + * being mutated via the corresponding helpers. + */ +noinline int bpf_map_trace_update_elem(struct bpf_map *map, void *key, + void *value, u64 map_flags); + +noinline int bpf_map_trace_delete_elem(struct bpf_map *map, void *key); + From patchwork Tue Nov 2 02:14:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Burton X-Patchwork-Id: 12597747 X-Patchwork-Delegate: bpf@iogearbox.net 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 mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0BC4FC4332F for ; Tue, 2 Nov 2021 02:14:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E7C3D60F0F for ; Tue, 2 Nov 2021 02:14:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229799AbhKBCRZ (ORCPT ); Mon, 1 Nov 2021 22:17:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46046 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229702AbhKBCRY (ORCPT ); Mon, 1 Nov 2021 22:17:24 -0400 Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1D07EC061714; Mon, 1 Nov 2021 19:14:50 -0700 (PDT) Received: by mail-pl1-x62a.google.com with SMTP id p18so10309858plf.13; Mon, 01 Nov 2021 19:14:50 -0700 (PDT) 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=7m4qvg/KmWRogURZVeoJWaji5c98J68mkI0Sglgv//U=; b=WgY33ytjLb87b27YOg8pYK7mQKjrzU+NkNtfuL97vfTeUfiPiLaSQbQuxpV/UiC7f9 bDeuq/jnAmbQqLvQOgNQr++ZvGcnVSTbMIQ/ZmDdgdae4GnIyh5Rj22PzWph4nwJyosa R2Db+N09wsoxZNUF2z4W7sO0sOEyj6vpqCGGAuKV/fDpm5xPWDgZZ2un96nb5Fq+qGj8 03PKnIh/bnhOQETKiaADREJhadP2nv0TePgb8I2lCt+WNdKVwqOR3p1b5fmyRFI57r8O nTt/UceTXR82GYnUp2F7RyqlDVEUvLfEWctrhcyUIJOnAF5vQqyuQHP1KhNEWjesmUgx wR/w== 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=7m4qvg/KmWRogURZVeoJWaji5c98J68mkI0Sglgv//U=; b=e9qAOMrA4rpCZ2x4SfYg9yyV5+LoWe/xs9BHv5N+0x2r0q4iBZooLkXjk1xIkiHkZg rwC9X9KqCJvUXqE9052NqokqcaE6V+pAjBIWizfsp9ABVY5wWed1CtDtwXq7WhVMd6Nm RVzzxXgtLakkmw00Y9VWknuH8Kam+5MFC3n9gX0Qy4ellqxqteiZ39yDFHrxSF77uqAK IJt66BUZvp41MR0KZst4sIneykRp9YJoipySaSvR8WCrVWMEca2IhVgyV+G3pfa659DX 0KnSh/GHFtmaaYTBhFDXHHKwH1Pr+YprI+Iy8+kem1RoHi2JguuSdBan0JygDb5cNaXv 63rg== X-Gm-Message-State: AOAM533T8hsZQ0bdbsd4LQIDqJfvui413dInSiBsvb1ZHGBDeQCWqNF6 4tJACsORbKJbcbyae/r3DQ== X-Google-Smtp-Source: ABdhPJzr9VZqcMqoVEPpDjWanNECw5jO7FsrcxKEikAfJWYPbsnuSva+fKJ7bgJaRmois39+OVp2sQ== X-Received: by 2002:a17:90b:4a89:: with SMTP id lp9mr3175444pjb.6.1635819289648; Mon, 01 Nov 2021 19:14:49 -0700 (PDT) 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 j6sm14051446pgf.60.2021.11.01.19.14.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Nov 2021 19:14:49 -0700 (PDT) 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 Cc: Petar Penkov , Stanislav Fomichev , Joe Burton Subject: [RFC PATCH v3 2/3] bpf: Add selftests Date: Tue, 2 Nov 2021 02:14:31 +0000 Message-Id: <20211102021432.2807760-3-jevburton.kernel@gmail.com> X-Mailer: git-send-email 2.33.1.1089.g2158813163f-goog In-Reply-To: <20211102021432.2807760-1-jevburton.kernel@gmail.com> References: <20211102021432.2807760-1-jevburton.kernel@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC 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 | 166 ++++++++++++++++++ .../selftests/bpf/progs/bpf_map_trace.c | 95 ++++++++++ .../bpf/progs/bpf_map_trace_common.h | 12 ++ 3 files changed, 273 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/testing/selftests/bpf/prog_tests/map_trace.c new file mode 100644 index 000000000000..4b54a8e3769a --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/map_trace.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2021 Google */ +#include + +#include "bpf_map_trace.skel.h" +#include "progs/bpf_map_trace_common.h" + +#include +#include + +enum BoolOrErr { + TRUE = 0, + FALSE = 1, + ERROR = 2, +}; + +enum BoolOrErr percpu_key_is_set(struct bpf_map *map, uint32_t map_key) +{ + int num_cpus = libbpf_num_possible_cpus(); + uint64_t *percpu_map_val = NULL; + int map_fd = bpf_map__fd(map); + enum BoolOrErr ret = ERROR; + int err; + int i; + + if (!ASSERT_GE(num_cpus, 1, "get number of cpus")) + goto out; + + percpu_map_val = malloc(sizeof(*percpu_map_val) * num_cpus); + if (!ASSERT_NEQ(percpu_map_val, NULL, "allocate percpu map array")) + goto out; + + err = bpf_map_lookup_elem(map_fd, &map_key, percpu_map_val); + if (!ASSERT_EQ(err, 0, "map lookup update_elem")) + goto out; + + ret = FALSE; + for (i = 0; i < num_cpus; i++) + if (percpu_map_val[i] != 0) + ret = TRUE; + +out: + if (percpu_map_val != NULL) + free(percpu_map_val); + + return ret; +} + +enum BoolOrErr key_is_set(struct bpf_map *map, uint32_t map_key) +{ + int map_fd = bpf_map__fd(map); + uint32_t map_val; + int rc; + + rc = 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 == 0 ? FALSE : TRUE); +} + +void verify_map_contents(struct bpf_map_trace *skel) +{ + enum BoolOrErr rc_or_err; + struct bpf_map *map; + + map = skel->maps.array_map; + rc_or_err = key_is_set(map, ACCESS_LOC__TRACE_UPDATE); + if (!ASSERT_EQ(rc_or_err, TRUE, "array map updates are traced")) + return; + rc_or_err = key_is_set(map, ACCESS_LOC__TRACE_DELETE); + if (!ASSERT_EQ(rc_or_err, FALSE, "array map deletions are not traced")) + return; + + map = skel->maps.percpu_array_map; + rc_or_err = 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 = 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 = skel->maps.hash_map; + rc_or_err = key_is_set(map, ACCESS_LOC__TRACE_UPDATE); + if (!ASSERT_EQ(rc_or_err, TRUE, "hash map updates are traced")) + return; + rc_or_err = key_is_set(map, ACCESS_LOC__TRACE_DELETE); + if (!ASSERT_EQ(rc_or_err, TRUE, "hash map deletions are traced")) + return; + + map = skel->maps.percpu_hash_map; + rc_or_err = 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 = percpu_key_is_set(map, ACCESS_LOC__TRACE_DELETE); + if (!ASSERT_EQ(rc_or_err, TRUE, + "percpu hash map deletions are traced")) + return; + + map = skel->maps.lru_hash_map; + rc_or_err = 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 = key_is_set(map, ACCESS_LOC__TRACE_DELETE); + if (!ASSERT_EQ(rc_or_err, TRUE, "lru_hash map deletions are traced")) + return; + + map = skel->maps.percpu_lru_hash_map; + rc_or_err = 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 = 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 = 'a'; + int write_fd = -1; + int rc; + + /* + * Load and attach programs. + */ + skel = bpf_map_trace__open_and_load(); + if (!ASSERT_NEQ(skel, NULL, "open/load skeleton")) + return; + + rc = bpf_map_trace__attach(skel); + if (!ASSERT_EQ(rc, 0, "attach skeleton")) + goto out; + + /* + * Invoke core BPF program. + */ + write_fd = open("/tmp/map_trace_test_file", O_CREAT | O_WRONLY); + if (!ASSERT_GE(rc, 0, "open tmp file for writing")) + goto out; + + bytes_written = 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 != -1) + close(write_fd); +} + +void test_map_trace(void) +{ + map_trace_test(); +} + diff --git a/tools/testing/selftests/bpf/progs/bpf_map_trace.c b/tools/testing/selftests/bpf/progs/bpf_map_trace.c new file mode 100644 index 000000000000..6135cd86b521 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/bpf_map_trace.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2021 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 = location; + u32 val = 1; + + bpf_map_update_elem(map, &key, &val, /*flags=*/0); +} + +static inline void log_location(struct bpf_map *map, + enum MapAccessLocations location) +{ + if (map == &array_map) + __log_location(&array_map, location); + if (map == &percpu_array_map) + __log_location(&percpu_array_map, location); + if (map == &hash_map) + __log_location(&hash_map, location); + if (map == &percpu_hash_map) + __log_location(&percpu_hash_map, location); + if (map == &lru_hash_map) + __log_location(&lru_hash_map, location); + if (map == &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 = ACCESS_LOC__APP; + u32 val = 1; + + bpf_map_update_elem(map, &key, &val, /*flags=*/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/tools/testing/selftests/bpf/progs/bpf_map_trace_common.h new file mode 100644 index 000000000000..3aac75953508 --- /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) 2021 Google */ +#pragma once + +enum MapAccessLocations { + ACCESS_LOC__APP, + ACCESS_LOC__TRACE_UPDATE, + ACCESS_LOC__TRACE_DELETE, + + __ACCESS_LOC__MAX, +}; + From patchwork Tue Nov 2 02:14:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joe Burton X-Patchwork-Id: 12597751 X-Patchwork-Delegate: bpf@iogearbox.net 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 mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 85C2DC433F5 for ; Tue, 2 Nov 2021 02:14:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 672C160EBC for ; Tue, 2 Nov 2021 02:14:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229702AbhKBCR0 (ORCPT ); Mon, 1 Nov 2021 22:17:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46050 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229458AbhKBCRY (ORCPT ); Mon, 1 Nov 2021 22:17:24 -0400 Received: from mail-pl1-x62f.google.com (mail-pl1-x62f.google.com [IPv6:2607:f8b0:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A7979C061714; Mon, 1 Nov 2021 19:14:50 -0700 (PDT) Received: by mail-pl1-x62f.google.com with SMTP id n8so2957953plf.4; Mon, 01 Nov 2021 19:14:50 -0700 (PDT) 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=NhBMumLpO+4VEHM/wnvo6pe26TNZVcNB5BthcOG/rxs=; b=VJDC5v8Ee9f05lkZHJHVQ6biOKIyGfhDyt7wgw7FZ+vS5sWtYSF+1VEZiQJ9mtcjJi gMd6bd10oKt3M6mEgdF62Eimww7dnSnz26A8+EiZ8uDI42IBuFQjN6T5HOkHu/lWzaJq n0aCdT9HySN9PjUmbiCp46iqHgd3rC6gB655TGvgs3Vyka19pQJ9pP6hVwmRfGP/lFV6 d5nEM5z3S8nZ4fnkIp/7/pCGZNtLqvicMfpgEcar3iQ3T3X67hyqP2PHLS82Ul9l02Qy fAm0HfTgbr8W3UUNJP13JbAfkvN+xvXdsrdI1ogVBGAXzmr17cwh+gOE7aqJYFNO0Ilr Zr6w== 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=NhBMumLpO+4VEHM/wnvo6pe26TNZVcNB5BthcOG/rxs=; b=gqIn70tsf0nmDtKz7DIStriNdFqE80XDQJKayYkjvW0wc/VOOnczZTvFaamO5fNX9Q XNRPno399wBmQuREsaEmbPQ7bPLxC0zMAV/MbPiQTRrSxOfDwz0cutOehDMSOhnwfyST 7gteQPrCdqoMjloby6alxFF0X1w/4n4k60M8ZHKsmN45iTNyOkRpyWSDhKoLupUAUokV p1l0g2/o+Bq3SyUCwky9aJi+LkPSd/wtMQaLMN0bD9QCIj/rqjCMQ164itPxMH/dIo8d bMC5nteoFvr+mZ9bV3sZju2UHzaMvLrUHUig4ylhXr6/Jpn509J1yorBDaQ39O6JTjAO Qpng== X-Gm-Message-State: AOAM531Fla8NUChcSrYvgzULNj1b+tZ5pz3YAONcYsyzZRegbZdhB5vG SJYUOZNMYC+GXf9RONLTmA== X-Google-Smtp-Source: ABdhPJwQuMnb9/H8XaTFROI/T6iTQBGLF4LYi1cg4BrLSlZyKFLDFsnVzh33WFTZCD/Fzs8y2EsKAg== X-Received: by 2002:a17:902:aa08:b0:13f:eb2e:8ce8 with SMTP id be8-20020a170902aa0800b0013feb2e8ce8mr29206903plb.0.1635819290052; Mon, 01 Nov 2021 19:14:50 -0700 (PDT) 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 j6sm14051446pgf.60.2021.11.01.19.14.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 01 Nov 2021 19:14:49 -0700 (PDT) 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 Cc: Petar Penkov , Stanislav Fomichev , Joe Burton Subject: [RFC PATCH v3 3/3] bpf: Add real world example for map tracing Date: Tue, 2 Nov 2021 02:14:32 +0000 Message-Id: <20211102021432.2807760-4-jevburton.kernel@gmail.com> X-Mailer: git-send-email 2.33.1.1089.g2158813163f-goog In-Reply-To: <20211102021432.2807760-1-jevburton.kernel@gmail.com> References: <20211102021432.2807760-1-jevburton.kernel@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net X-Patchwork-State: RFC 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 | 256 ++++++++++++++++++ .../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, 492 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_common.h create mode 100644 tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_migration.c create mode 100644 tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_new.c create mode 100644 tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_old.c diff --git a/tools/testing/selftests/bpf/prog_tests/map_trace.c b/tools/testing/selftests/bpf/prog_tests/map_trace.c index 4b54a8e3769a..922af9ab06e9 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 #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" #include @@ -159,8 +162,261 @@ void map_trace_test(void) close(write_fd); } +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 = bpf_program__attach( + migration_skel->progs.copy_on_write__update); + error = 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 = bpf_program__attach( + migration_skel->progs.copy_on_write__delete); + error = 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 = bpf_map__fd(migration_skel->maps.old_map); + + memset(&iter_opts, 0, sizeof(iter_opts)); + iter_opts.sz = sizeof(iter_opts); + iter_opts.link_info = &iter_link_info; + iter_opts.link_info_len = sizeof(iter_link_info); + *iter_link = bpf_program__attach_iter( + migration_skel->progs.bulk_migration, &iter_opts); + error = 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 = malloc(sizeof(int) * num_files); + ssize_t bytes_written; + const char buf = 'a'; + size_t i, j; + int ret = 0; + + if (fds == NULL) + return 1; + + for (i = 0; i < num_files; i++) { + fds[i] = open(path, O_WRONLY | O_CREAT); + + if (fds[i] < 0) { + ret = 2; + break; + } + bytes_written = write(fds[i], &buf, sizeof(buf)); + if (bytes_written != sizeof(buf)) { + ret = 3; + break; + } + } + for (j = 0; j < i; j++) + close(fds[j]); + return ret; +} + +void real_world_example(void) +{ + struct bpf_map_trace_real_world_migration *migration_skel = NULL; + int file_fd_should_write = -1, file_fd_should_not_write = -1; + struct bpf_map_trace_real_world_new *new_skel = NULL; + struct bpf_map_trace_real_world_old *old_skel = NULL; + struct bpf_link *map_trace_link_update = NULL; + struct bpf_link *map_trace_link_delete = NULL; + struct bpf_link *iter_link = NULL; + const bool enable_filtering = 1; + const uint32_t pid = getpid(); + uint32_t max_open_files; + char file_buf = 'a'; + int iter_fd = -1; + char iter_buf[1]; + int rc; + + /* + * Begin by loading and attaching the old version of our program. + */ + old_skel = bpf_map_trace_real_world_old__open_and_load(); + if (!ASSERT_NEQ(old_skel, NULL, "open/load old skeleton")) + return; + rc = 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 = bpf_map_update_elem(bpf_map__fd(old_skel->maps.filtered_pids), + &pid, &enable_filtering, /*flags=*/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 = 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 = 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 = bpf_map_trace_real_world_migration__open(); + if (!ASSERT_NEQ(migration_skel, NULL, "open migration skeleton")) + goto out; + rc = 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 = 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 = bpf_map_trace_real_world_migration__load(migration_skel); + if (!ASSERT_EQ(rc, 0, "load migration skeleton")) + goto out; + rc = 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 = 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 = bpf_iter_create(bpf_link__fd(iter_link)); + if (!ASSERT_GE(iter_fd, 0, "create iterator")) + goto out; + rc = read(iter_fd, &iter_buf, sizeof(iter_buf)); + if (!ASSERT_EQ(rc, 0, "execute map iterator")) + goto out; + rc = bpf_map_update_elem(bpf_map__fd(new_skel->maps.filtered_pids), + &pid, &enable_filtering, /*flags=*/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 = -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 = 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 = 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 = -1; + bpf_link__destroy(map_trace_link_update); + map_trace_link_update = NULL; + bpf_link__destroy(map_trace_link_delete); + map_trace_link_delete = NULL; + bpf_link__destroy(iter_link); + iter_link = NULL; + bpf_map_trace_real_world_migration__destroy(migration_skel); + migration_skel = NULL; + + /* + * Unload old programs. + */ + bpf_map_trace_real_world_old__destroy(old_skel); + old_skel = 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 = 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 = 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) { map_trace_test(); + real_world_example(); } diff --git a/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_common.h b/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_common.h new file mode 100644 index 000000000000..230610e1b5d5 --- /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) 2021 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 application + * 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 = (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF; + memset(&key, 0, sizeof(key)); + key.pid = pid; + key.fd = ret; + val.do_allow = true; + + if (ret < 0) + return 0; + + pid_is_filtered = 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=*/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 = PT_REGS_PARM1(regs); + struct allow_reads_value *val; + struct allow_reads_key key; + + memset(&key, 0, sizeof(key)); + key.pid = (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF; + key.fd = fd; + val = 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 = PT_REGS_PARM1(regs); + struct allow_reads_key key; + struct allow_reads_value val; + + memset(&key, 0, sizeof(key)); + key.pid = (bpf_get_current_pid_tgid() >> 32) & 0xFFFFFFFF; + key.fd = fd; + val.do_allow = true; + + bpf_map_delete_elem(&allow_reads, &key); + return 0; +} + +char _license[] SEC("license") = "GPL"; + diff --git a/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_migration.c b/tools/testing/selftests/bpf/progs/bpf_map_trace_real_world_migration.c new file mode 100644 index 000000000000..18d61aa7ce4f --- /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) 2021 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 = {}; + struct allow_reads_key__new new_key = {}; + char old_value = 0; + + if (bpf_probe_read(&old_key, sizeof(old_key), key)) + return; /* Could write to a map here */ + if (bpf_probe_read(&old_value, sizeof(old_value), value)) + return; /* Could write to a map here */ + + new_key.pid = old_key.pid; + new_key.fd = old_key.fd; + + bpf_map_update_elem(&new_map, &new_key, &old_value, /*flags=*/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 == &old_map) + read_migrate_write(key, value); + return 0; +} + +static inline void read_migrate_delete(void *key) +{ + struct allow_reads_key__old old_key = {}; + struct allow_reads_key__new new_key = {}; + + if (bpf_probe_read(&old_key, sizeof(old_key), key)) + return; /* Could write to a map here */ + + new_key.pid = old_key.pid; + new_key.fd = 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 == &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") = "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..9b7c4ca1deed --- /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) 2021 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..9f0bdd7baf71 --- /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) 2021 Google */ +#define OLD_VERSION +#include "bpf_map_trace_real_world_common.h" +