[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20230913123154.94264-1-gerhorst@amazon.de>
Date: Wed, 13 Sep 2023 12:31:54 +0000
From: Luis Gerhorst <gerhorst@...zon.de>
To: <alexei.starovoitov@...il.com>
CC: <andrii@...nel.org>, <ast@...nel.org>, <bpf@...r.kernel.org>,
<daniel@...earbox.net>, <haoluo@...gle.com>,
<john.fastabend@...il.com>, <jolsa@...nel.org>,
<kpsingh@...nel.org>, <laoar.shao@...il.com>,
<martin.lau@...ux.dev>, <sdf@...gle.com>, <song@...nel.org>,
<yonghong.song@...ux.dev>, <mykolal@...com>, <shuah@...nel.org>,
<gerhorst@...zon.de>, <iii@...ux.ibm.com>,
<linux-kselftest@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
Luis Gerhorst <gerhorst@...fau.de>
Subject: [PATCH 3/3] selftests/bpf: Add selftest for packet-pointer Spectre v1 gadget
When allowing speculative leaks by enabling packet pointer accesses
without CAP_PERFMON (i.e., without having [1] reverted):
$ tools/testing/selftests/bpf/test_progs --name=tc_bpf
tc_bpf_non_root:PASS:set_cap_bpf_cap_net_admin 0 nsec
tc_bpf_non_root:PASS:disable_cap_sys_admin 0 nsec
tc_bpf_non_root:FAIL:test_tc_bpf__open_and_load unexpected pointer: 0x55bbd81969a0
Summary: 0/1 PASSED, 0 SKIPPED, 1 FAILED
With [1] reverted:
$ tools/testing/selftests/bpf/test_progs --name=tc_bpf
#238/1 tc_bpf/tc_bpf_root:OK
#238/2 tc_bpf/tc_bpf_non_root:OK
#238 tc_bpf:OK
Summary: 1/2 PASSED, 0 SKIPPED, 0 FAILED
[1] d75e30dddf73449bc2d10bb8e2f1a2c446bc67a2 ("bpf: Fix issue in verifying allow_ptr_leaks")
Signed-off-by: Luis Gerhorst <gerhorst@...zon.de>
Signed-off-by: Luis Gerhorst <gerhorst@...fau.de>
Based-on-patch-by: Yafang Shao <laoar.shao@...il.com>
---
.../testing/selftests/bpf/prog_tests/tc_bpf.c | 37 +++++++-
.../testing/selftests/bpf/progs/test_tc_bpf.c | 95 +++++++++++++++++++
2 files changed, 131 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/tc_bpf.c b/tools/testing/selftests/bpf/prog_tests/tc_bpf.c
index e873766276d1..5319cb94a0ae 100644
--- a/tools/testing/selftests/bpf/prog_tests/tc_bpf.c
+++ b/tools/testing/selftests/bpf/prog_tests/tc_bpf.c
@@ -3,6 +3,7 @@
#include <test_progs.h>
#include <linux/pkt_cls.h>
+#include "cap_helpers.h"
#include "test_tc_bpf.skel.h"
#define LO_IFINDEX 1
@@ -327,7 +328,7 @@ static int test_tc_bpf_api(struct bpf_tc_hook *hook, int fd)
return 0;
}
-void test_tc_bpf(void)
+void tc_bpf_root(void)
{
DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = LO_IFINDEX,
.attach_point = BPF_TC_INGRESS);
@@ -393,3 +394,37 @@ void test_tc_bpf(void)
}
test_tc_bpf__destroy(skel);
}
+
+void tc_bpf_non_root(void)
+{
+ struct test_tc_bpf *skel = NULL;
+ __u64 caps = 0;
+ int ret;
+
+ /* In case CAP_BPF and CAP_PERFMON is not set */
+ ret = cap_enable_effective(1ULL << CAP_BPF | 1ULL << CAP_NET_ADMIN, &caps);
+ if (!ASSERT_OK(ret, "set_cap_bpf_cap_net_admin"))
+ return;
+ ret = cap_disable_effective(1ULL << CAP_SYS_ADMIN | 1ULL << CAP_PERFMON, NULL);
+ if (!ASSERT_OK(ret, "disable_cap_sys_admin"))
+ goto restore_cap;
+
+ skel = test_tc_bpf__open_and_load();
+ if (!ASSERT_ERR_PTR(skel, "test_tc_bpf__open_and_load"))
+ goto destroy;
+
+ goto restore_cap;
+destroy:
+ test_tc_bpf__destroy(skel);
+restore_cap:
+ if (caps)
+ cap_enable_effective(caps, NULL);
+}
+
+void test_tc_bpf(void)
+{
+ if (test__start_subtest("tc_bpf_root"))
+ tc_bpf_root();
+ if (test__start_subtest("tc_bpf_non_root"))
+ tc_bpf_non_root();
+}
diff --git a/tools/testing/selftests/bpf/progs/test_tc_bpf.c b/tools/testing/selftests/bpf/progs/test_tc_bpf.c
index d28ca8d1f3d0..3b3f9ce6b9d4 100644
--- a/tools/testing/selftests/bpf/progs/test_tc_bpf.c
+++ b/tools/testing/selftests/bpf/progs/test_tc_bpf.c
@@ -2,6 +2,8 @@
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
+#include <linux/if_ether.h>
+#include <linux/ip.h>
/* Dummy prog to test TC-BPF API */
@@ -10,3 +12,96 @@ int cls(struct __sk_buff *skb)
{
return 0;
}
+
+/* Prog to verify tc-bpf without cap_sys_admin and cap_perfmon is rejected as
+ * required to prevent Spectre v1 using CPU multiplication port contention
+ * side-channel. This is not a full exploit but rather a PoC for x86_64. With
+ * extensions to the verifier's mitigations this may become obsolete.
+ *
+ * This should compile to the following bytecode if the kernel would allow
+ * unprivileged packet pointer accesses:
+ *
+
+0000000000000000 <pkt_ptr>:
+ 0: b4 00 00 00 00 00 00 00 w0 = 0
+ 1: 61 12 50 00 00 00 00 00 r2 = *(u32 *)(r1 + 80)
+ 2: 61 11 4c 00 00 00 00 00 r1 = *(u32 *)(r1 + 76)
+ 3: bf 13 00 00 00 00 00 00 r3 = r1
+ 4: 07 03 00 00 22 00 00 00 r3 += 34
+ 5: bd 23 07 00 00 00 00 00 if r3 <= r2 goto +7 <LBB1_3>
+ 6: 71 10 0e 00 00 00 00 00 r0 = *(u8 *)(r1 + 14)
+ 7: 64 00 00 00 18 00 00 00 w0 <<= 24
+ 8: c4 00 00 00 18 00 00 00 w0 s>>= 24
+ 9: bc 01 00 00 00 00 00 00 w1 = w0
+ 10: 54 01 00 00 01 00 00 00 w1 &= 1
+ 11: 16 01 01 00 00 00 00 00 if w1 == 0 goto +1 <LBB1_3>
+ 12: 24 00 00 00 61 00 00 00 w0 *= 97
+
+0000000000000068 <LBB1_3>:
+ 13: 95 00 00 00 00 00 00 00 exit
+
+ *
+ * Which should in turn translate to this x86_64 assembly with !allow_ptr_leaks
+ * and !bypass_spec_v1:
+ *
+
+int pkt_ptr(struct __sk_buff * skb):
+bpf_prog_7c3834bad32f2b0f_pkt_ptr:
+; int pkt_ptr(struct __sk_buff *skb)
+ 0: endbr64
+ 4: nopl 0x0(%rax,%rax,1)
+ 9: xchg %ax,%ax
+ b: push %rbp
+ c: mov %rsp,%rbp
+ f: endbr64
+ 13: xor %eax,%eax
+; if ((long)(iph + 1) > (long)skb->data_end)
+ 15: mov 0x50(%rdi),%rsi
+; struct iphdr *iph = (void *)(long)skb->data + sizeof(struct ethhdr);
+ 19: mov 0xc8(%rdi),%rdi
+; if ((long)(iph + 1) > (long)skb->data_end)
+ 20: mov %rdi,%rdx
+ 23: add $0x22,%rdx
+; if ((long)(iph + 1) > (long)skb->data_end)
+ 27: cmp %rsi,%rdx
+ 2a: ja 0x0000000000000043
+; char secret = *((char *) iph);
+ 2c: movzbq 0xe(%rdi),%rax
+ 31: shl $0x18,%eax
+ 34: sar $0x18,%eax
+; if (secret & 1) {
+ 37: mov %eax,%edi
+ 39: and $0x1,%edi
+; if (secret & 1) {
+ 3c: test %edi,%edi
+ 3e: je 0x0000000000000043
+ 40: imul $0x61,%eax,%eax
+; }
+ 43: leaveq
+ 44: retq
+
+ *
+ */
+SEC("tcx/ingress")
+int pkt_ptr(struct __sk_buff *skb)
+{
+ struct iphdr *iph = (void *)(long)skb->data + sizeof(struct ethhdr);
+
+ /* Branch to be speculatively bypassed. */
+ if ((long)(iph + 1) > (long)skb->data_end)
+ return 0;
+
+ /* Speculative access to be prevented. */
+ char secret = *((char *) iph);
+
+ /* Leak the first bit of the secret value that lies behind data_end to a
+ * SMP silbling thread that also executes imul instructions. If the bit
+ * is 1, the silbling will experience a slowdown. */
+ long long x = secret;
+ if (secret & 1) {
+ x *= 97;
+ }
+
+ /* To prevent optimization. */
+ return x;
+}
--
2.40.1
Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
Powered by blists - more mailing lists