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