1 // SPDX-License-Identifier: GPL-2.0
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <ctype.h>
6 #include <time.h>
7 #include <errno.h>
8 #include <unistd.h>
9 #include <string.h>
10 #include <sched.h>
11 #include <limits.h>
12 #include <assert.h>
13 
14 #include <sys/socket.h>
15 
16 #include <linux/filter.h>
17 #include <linux/bpf.h>
18 #include <linux/if_alg.h>
19 
20 #include <bpf/bpf.h>
21 
22 #include "../../../include/linux/filter.h"
23 #include "bpf_rlimit.h"
24 #include "testing_helpers.h"
25 
26 static struct bpf_insn prog[BPF_MAXINSNS];
27 
28 static void bpf_gen_imm_prog(unsigned int insns, int fd_map)
29 {
30 	int i;
31 
32 	srand(time(NULL));
33 	for (i = 0; i < insns; i++)
34 		prog[i] = BPF_ALU64_IMM(BPF_MOV, i % BPF_REG_10, rand());
35 	prog[i - 1] = BPF_EXIT_INSN();
36 }
37 
38 static void bpf_gen_map_prog(unsigned int insns, int fd_map)
39 {
40 	int i, j = 0;
41 
42 	for (i = 0; i + 1 < insns; i += 2) {
43 		struct bpf_insn tmp[] = {
44 			BPF_LD_MAP_FD(j++ % BPF_REG_10, fd_map)
45 		};
46 
47 		memcpy(&prog[i], tmp, sizeof(tmp));
48 	}
49 	if (insns % 2 == 0)
50 		prog[insns - 2] = BPF_ALU64_IMM(BPF_MOV, i % BPF_REG_10, 42);
51 	prog[insns - 1] = BPF_EXIT_INSN();
52 }
53 
54 static int bpf_try_load_prog(int insns, int fd_map,
55 			     void (*bpf_filler)(unsigned int insns,
56 						int fd_map))
57 {
58 	int fd_prog;
59 
60 	bpf_filler(insns, fd_map);
61 	fd_prog = bpf_test_load_program(BPF_PROG_TYPE_SCHED_CLS, prog, insns, "", 0,
62 				   NULL, 0);
63 	assert(fd_prog > 0);
64 	if (fd_map > 0)
65 		bpf_filler(insns, 0);
66 	return fd_prog;
67 }
68 
69 static int __hex2bin(char ch)
70 {
71 	if ((ch >= '0') && (ch <= '9'))
72 		return ch - '0';
73 	ch = tolower(ch);
74 	if ((ch >= 'a') && (ch <= 'f'))
75 		return ch - 'a' + 10;
76 	return -1;
77 }
78 
79 static int hex2bin(uint8_t *dst, const char *src, size_t count)
80 {
81 	while (count--) {
82 		int hi = __hex2bin(*src++);
83 		int lo = __hex2bin(*src++);
84 
85 		if ((hi < 0) || (lo < 0))
86 			return -1;
87 		*dst++ = (hi << 4) | lo;
88 	}
89 	return 0;
90 }
91 
92 static void tag_from_fdinfo(int fd_prog, uint8_t *tag, uint32_t len)
93 {
94 	const int prefix_len = sizeof("prog_tag:\t") - 1;
95 	char buff[256];
96 	int ret = -1;
97 	FILE *fp;
98 
99 	snprintf(buff, sizeof(buff), "/proc/%d/fdinfo/%d", getpid(),
100 		 fd_prog);
101 	fp = fopen(buff, "r");
102 	assert(fp);
103 
104 	while (fgets(buff, sizeof(buff), fp)) {
105 		if (strncmp(buff, "prog_tag:\t", prefix_len))
106 			continue;
107 		ret = hex2bin(tag, buff + prefix_len, len);
108 		break;
109 	}
110 
111 	fclose(fp);
112 	assert(!ret);
113 }
114 
115 static void tag_from_alg(int insns, uint8_t *tag, uint32_t len)
116 {
117 	static const struct sockaddr_alg alg = {
118 		.salg_family	= AF_ALG,
119 		.salg_type	= "hash",
120 		.salg_name	= "sha1",
121 	};
122 	int fd_base, fd_alg, ret;
123 	ssize_t size;
124 
125 	fd_base = socket(AF_ALG, SOCK_SEQPACKET, 0);
126 	assert(fd_base > 0);
127 
128 	ret = bind(fd_base, (struct sockaddr *)&alg, sizeof(alg));
129 	assert(!ret);
130 
131 	fd_alg = accept(fd_base, NULL, 0);
132 	assert(fd_alg > 0);
133 
134 	insns *= sizeof(struct bpf_insn);
135 	size = write(fd_alg, prog, insns);
136 	assert(size == insns);
137 
138 	size = read(fd_alg, tag, len);
139 	assert(size == len);
140 
141 	close(fd_alg);
142 	close(fd_base);
143 }
144 
145 static void tag_dump(const char *prefix, uint8_t *tag, uint32_t len)
146 {
147 	int i;
148 
149 	printf("%s", prefix);
150 	for (i = 0; i < len; i++)
151 		printf("%02x", tag[i]);
152 	printf("\n");
153 }
154 
155 static void tag_exit_report(int insns, int fd_map, uint8_t *ftag,
156 			    uint8_t *atag, uint32_t len)
157 {
158 	printf("Program tag mismatch for %d insns%s!\n", insns,
159 	       fd_map < 0 ? "" : " with map");
160 
161 	tag_dump("  fdinfo result: ", ftag, len);
162 	tag_dump("  af_alg result: ", atag, len);
163 	exit(1);
164 }
165 
166 static void do_test(uint32_t *tests, int start_insns, int fd_map,
167 		    void (*bpf_filler)(unsigned int insns, int fd))
168 {
169 	int i, fd_prog;
170 
171 	for (i = start_insns; i <= BPF_MAXINSNS; i++) {
172 		uint8_t ftag[8], atag[sizeof(ftag)];
173 
174 		fd_prog = bpf_try_load_prog(i, fd_map, bpf_filler);
175 		tag_from_fdinfo(fd_prog, ftag, sizeof(ftag));
176 		tag_from_alg(i, atag, sizeof(atag));
177 		if (memcmp(ftag, atag, sizeof(ftag)))
178 			tag_exit_report(i, fd_map, ftag, atag, sizeof(ftag));
179 
180 		close(fd_prog);
181 		sched_yield();
182 		(*tests)++;
183 	}
184 }
185 
186 int main(void)
187 {
188 	LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_NO_PREALLOC);
189 	uint32_t tests = 0;
190 	int i, fd_map;
191 
192 	fd_map = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof(int),
193 				sizeof(int), 1, &opts);
194 	assert(fd_map > 0);
195 
196 	for (i = 0; i < 5; i++) {
197 		do_test(&tests, 2, -1,     bpf_gen_imm_prog);
198 		do_test(&tests, 3, fd_map, bpf_gen_map_prog);
199 	}
200 
201 	printf("test_tag: OK (%u tests)\n", tests);
202 	close(fd_map);
203 	return 0;
204 }
205