xref: /linux/tools/testing/selftests/bpf/progs/crypto_sanity.c (revision 872cc6abda1507429b97cc7b42a9dae51ee0a668)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
3 
4 #include "vmlinux.h"
5 #include "bpf_tracing_net.h"
6 #include <bpf/bpf_helpers.h>
7 #include <bpf/bpf_endian.h>
8 #include <bpf/bpf_tracing.h>
9 #include "bpf_misc.h"
10 #include "bpf_kfuncs.h"
11 #include "crypto_common.h"
12 
13 /*
14  * key[] and algo[] are 8-byte aligned and 'params' is kept off the stack to
15  * work around an LLVM code generation bug. clang lowers the memcpy() of these
16  * byte-aligned globals into a per-byte load/store sequence staged on the stack,
17  * and additionally materializes the on-stack 'struct bpf_crypto_params' twice.
18  * Both blow the 512-byte BPF stack limit. Aligning the sources lets clang copy
19  * word-wise, and a global 'params' removes the large object from the stack.
20  */
21 unsigned char key[256] __attribute__((aligned(8))) = {};
22 u16 udp_test_port = 7777;
23 u32 authsize, key_len;
24 char algo[128] __attribute__((aligned(8))) = {};
25 char dst[16] = {}, dst_bad[8] = {};
26 static struct bpf_crypto_params params;
27 int status;
28 
29 static int skb_dynptr_validate(struct __sk_buff *skb, struct bpf_dynptr *psrc)
30 {
31 	struct ipv6hdr ip6h;
32 	struct udphdr udph;
33 	u32 offset;
34 
35 	if (skb->protocol != __bpf_constant_htons(ETH_P_IPV6))
36 		return -1;
37 
38 	if (bpf_skb_load_bytes(skb, ETH_HLEN, &ip6h, sizeof(ip6h)))
39 		return -1;
40 
41 	if (ip6h.nexthdr != IPPROTO_UDP)
42 		return -1;
43 
44 	if (bpf_skb_load_bytes(skb, ETH_HLEN + sizeof(ip6h), &udph, sizeof(udph)))
45 		return -1;
46 
47 	if (udph.dest != __bpf_htons(udp_test_port))
48 		return -1;
49 
50 	offset = ETH_HLEN + sizeof(ip6h) + sizeof(udph);
51 	if (skb->len < offset + 16)
52 		return -1;
53 
54 	/* let's make sure that 16 bytes of payload are in the linear part of skb */
55 	bpf_skb_pull_data(skb, offset + 16);
56 	bpf_dynptr_from_skb(skb, 0, psrc);
57 	bpf_dynptr_adjust(psrc, offset, offset + 16);
58 
59 	return 0;
60 }
61 
62 SEC("syscall")
63 int skb_crypto_setup(void *ctx)
64 {
65 	struct bpf_crypto_ctx *cctx;
66 	int err;
67 
68 	status = 0;
69 	if (key_len > 256) {
70 		status = -EINVAL;
71 		return 0;
72 	}
73 
74 	__builtin_memcpy(&params.type, "skcipher", sizeof("skcipher"));
75 	params.key_len = key_len;
76 	params.authsize = authsize;
77 	__builtin_memcpy(&params.algo, algo, sizeof(algo));
78 	__builtin_memcpy(&params.key, key, sizeof(key));
79 
80 	cctx = bpf_crypto_ctx_create(&params, sizeof(params), &err);
81 	if (!cctx) {
82 		status = err;
83 		return 0;
84 	}
85 
86 	err = crypto_ctx_insert(cctx);
87 	if (err && err != -EEXIST)
88 		status = err;
89 	return 0;
90 }
91 
92 SEC("tc")
93 int decrypt_sanity(struct __sk_buff *skb)
94 {
95 	struct __crypto_ctx_value *v;
96 	struct bpf_crypto_ctx *ctx;
97 	struct bpf_dynptr psrc, pdst;
98 	int err;
99 
100 	status = 0;
101 	err = skb_dynptr_validate(skb, &psrc);
102 	if (err < 0) {
103 		status = err;
104 		return TC_ACT_SHOT;
105 	}
106 
107 	v = crypto_ctx_value_lookup();
108 	if (!v) {
109 		status = -ENOENT;
110 		return TC_ACT_SHOT;
111 	}
112 
113 	ctx = v->ctx;
114 	if (!ctx) {
115 		status = -ENOENT;
116 		return TC_ACT_SHOT;
117 	}
118 
119 	/* Check also bad case where the dst buffer is smaller than the
120 	 * skb's linear section.
121 	 */
122 	bpf_dynptr_from_mem(dst_bad, sizeof(dst_bad), 0, &pdst);
123 	status = bpf_crypto_decrypt(ctx, &psrc, &pdst, NULL);
124 	if (!status)
125 		status = -EIO;
126 	if (status != -EINVAL)
127 		goto err;
128 
129 	/* dst is a global variable to make testing part easier to check.
130 	 * In real production code, a percpu map should be used to store
131 	 * the result.
132 	 */
133 	bpf_dynptr_from_mem(dst, sizeof(dst), 0, &pdst);
134 	status = bpf_crypto_decrypt(ctx, &psrc, &pdst, NULL);
135 err:
136 	return TC_ACT_SHOT;
137 }
138 
139 SEC("tc")
140 int encrypt_sanity(struct __sk_buff *skb)
141 {
142 	struct __crypto_ctx_value *v;
143 	struct bpf_crypto_ctx *ctx;
144 	struct bpf_dynptr psrc, pdst;
145 	int err;
146 
147 	status = 0;
148 	err = skb_dynptr_validate(skb, &psrc);
149 	if (err < 0) {
150 		status = err;
151 		return TC_ACT_SHOT;
152 	}
153 
154 	v = crypto_ctx_value_lookup();
155 	if (!v) {
156 		status = -ENOENT;
157 		return TC_ACT_SHOT;
158 	}
159 
160 	ctx = v->ctx;
161 	if (!ctx) {
162 		status = -ENOENT;
163 		return TC_ACT_SHOT;
164 	}
165 
166 	/* Check also bad case where the dst buffer is smaller than the
167 	 * skb's linear section.
168 	 */
169 	bpf_dynptr_from_mem(dst_bad, sizeof(dst_bad), 0, &pdst);
170 	status = bpf_crypto_encrypt(ctx, &psrc, &pdst, NULL);
171 	if (!status)
172 		status = -EIO;
173 	if (status != -EINVAL)
174 		goto err;
175 
176 	/* dst is a global variable to make testing part easier to check.
177 	 * In real production code, a percpu map should be used to store
178 	 * the result.
179 	 */
180 	bpf_dynptr_from_mem(dst, sizeof(dst), 0, &pdst);
181 	status = bpf_crypto_encrypt(ctx, &psrc, &pdst, NULL);
182 err:
183 	return TC_ACT_SHOT;
184 }
185 
186 char __license[] SEC("license") = "GPL";
187