Bundle libseccomp 2.3.1
[linux-seccomp.git] / libseccomp / src / gen_pfc.c
CommitLineData
8befd5cc
MG
1/**
2 * Seccomp Pseudo Filter Code (PFC) Generator
3 *
4 * Copyright (c) 2012 Red Hat <pmoore@redhat.com>
5 * Author: Paul Moore <paul@paul-moore.com>
6 */
7
8/*
9 * This library is free software; you can redistribute it and/or modify it
10 * under the terms of version 2.1 of the GNU Lesser General Public License as
11 * published by the Free Software Foundation.
12 *
13 * This library is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this library; if not, see <http://www.gnu.org/licenses>.
20 */
21
22#include <errno.h>
23#include <inttypes.h>
24#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
27#include <unistd.h>
28
29/* NOTE: needed for the arch->token decoding in _pfc_arch() */
30#include <linux/audit.h>
31
32#include <seccomp.h>
33
34#include "arch.h"
35#include "db.h"
36#include "gen_pfc.h"
37
38struct pfc_sys_list {
39 struct db_sys_list *sys;
40 struct pfc_sys_list *next;
41};
42
43/* XXX - we should check the fprintf() return values */
44
45/**
46 * Display a string representation of the architecture
47 * @param arch the architecture definition
48 */
49static const char *_pfc_arch(const struct arch_def *arch)
50{
51 switch (arch->token) {
52 case SCMP_ARCH_X86:
53 return "x86";
54 case SCMP_ARCH_X86_64:
55 return "x86_64";
56 case SCMP_ARCH_X32:
57 return "x32";
58 case SCMP_ARCH_ARM:
59 return "arm";
60 case SCMP_ARCH_AARCH64:
61 return "aarch64";
62 case SCMP_ARCH_MIPS:
63 return "mips";
64 case SCMP_ARCH_MIPSEL:
65 return "mipsel";
66 case SCMP_ARCH_MIPS64:
67 return "mips64";
68 case SCMP_ARCH_MIPSEL64:
69 return "mipsel64";
70 case SCMP_ARCH_MIPS64N32:
71 return "mips64n32";
72 case SCMP_ARCH_MIPSEL64N32:
73 return "mipsel64n32";
74 case SCMP_ARCH_PPC64:
75 return "ppc64";
76 case SCMP_ARCH_PPC64LE:
77 return "ppc64le";
78 case SCMP_ARCH_PPC:
79 return "ppc";
80 case SCMP_ARCH_S390X:
81 return "s390x";
82 case SCMP_ARCH_S390:
83 return "s390";
84 default:
85 return "UNKNOWN";
86 }
87}
88
89/**
90 * Display a string representation of the node argument
91 * @param fds the file stream to send the output
92 * @param arch the architecture definition
93 * @param node the node
94 */
95static void _pfc_arg(FILE *fds,
96 const struct arch_def *arch,
97 const struct db_arg_chain_tree *node)
98{
99 if (arch->size == ARCH_SIZE_64) {
100 if (arch_arg_offset_hi(arch, node->arg) == node->arg_offset)
101 fprintf(fds, "$a%d.hi32", node->arg);
102 else
103 fprintf(fds, "$a%d.lo32", node->arg);
104 } else
105 fprintf(fds, "$a%d", node->arg);
106}
107
108/**
109 * Display a string representation of the filter action
110 * @param fds the file stream to send the output
111 * @param action the action
112 */
113static void _pfc_action(FILE *fds, uint32_t action)
114{
115 switch (action & 0xffff0000) {
116 case SCMP_ACT_KILL:
117 fprintf(fds, "action KILL;\n");
118 break;
119 case SCMP_ACT_TRAP:
120 fprintf(fds, "action TRAP;\n");
121 break;
122 case SCMP_ACT_ERRNO(0):
123 fprintf(fds, "action ERRNO(%u);\n", (action & 0x0000ffff));
124 break;
125 case SCMP_ACT_TRACE(0):
126 fprintf(fds, "action TRACE(%u);\n", (action & 0x0000ffff));
127 break;
128 case SCMP_ACT_ALLOW:
129 fprintf(fds, "action ALLOW;\n");
130 break;
131 default:
132 fprintf(fds, "action 0x%x;\n", action);
133 }
134}
135
136/**
137 * Indent the output stream
138 * @param fds the file stream to send the output
139 * @param lvl the indentation level
140 *
141 * This function indents the output stream with whitespace based on the
142 * requested indentation level.
143 */
144static void _indent(FILE *fds, unsigned int lvl)
145{
146 while (lvl-- > 0)
147 fprintf(fds, " ");
148}
149
150/**
151 * Generate the pseudo filter code for an argument chain
152 * @param arch the architecture definition
153 * @param node the head of the argument chain
154 * @param lvl the indentation level
155 * @param fds the file stream to send the output
156 *
157 * This function generates the pseudo filter code representation of the given
158 * argument chain and writes it to the given output stream.
159 *
160 */
161static void _gen_pfc_chain(const struct arch_def *arch,
162 const struct db_arg_chain_tree *node,
163 unsigned int lvl, FILE *fds)
164{
165 const struct db_arg_chain_tree *c_iter;
166
167 /* get to the start */
168 c_iter = node;
169 while (c_iter->lvl_prv != NULL)
170 c_iter = c_iter->lvl_prv;
171
172 while (c_iter != NULL) {
173 /* comparison operation */
174 _indent(fds, lvl);
175 fprintf(fds, "if (");
176 _pfc_arg(fds, arch, c_iter);
177 switch (c_iter->op) {
178 case SCMP_CMP_EQ:
179 fprintf(fds, " == ");
180 break;
181 case SCMP_CMP_GE:
182 fprintf(fds, " >= ");
183 break;
184 case SCMP_CMP_GT:
185 fprintf(fds, " > ");
186 break;
187 case SCMP_CMP_MASKED_EQ:
188 fprintf(fds, " & 0x%.8x == ", c_iter->mask);
189 break;
190 case SCMP_CMP_NE:
191 case SCMP_CMP_LT:
192 case SCMP_CMP_LE:
193 default:
194 fprintf(fds, " ??? ");
195 }
196 fprintf(fds, "%u)\n", c_iter->datum);
197
198 /* true result */
199 if (c_iter->act_t_flg) {
200 _indent(fds, lvl + 1);
201 _pfc_action(fds, c_iter->act_t);
202 } else if (c_iter->nxt_t != NULL)
203 _gen_pfc_chain(arch, c_iter->nxt_t, lvl + 1, fds);
204
205 /* false result */
206 if (c_iter->act_f_flg) {
207 _indent(fds, lvl);
208 fprintf(fds, "else\n");
209 _indent(fds, lvl + 1);
210 _pfc_action(fds, c_iter->act_f);
211 } else if (c_iter->nxt_f != NULL) {
212 _indent(fds, lvl);
213 fprintf(fds, "else\n");
214 _gen_pfc_chain(arch, c_iter->nxt_f, lvl + 1, fds);
215 }
216
217 c_iter = c_iter->lvl_nxt;
218 }
219}
220
221/**
222 * Generate pseudo filter code for a syscall
223 * @param arch the architecture definition
224 * @param sys the syscall filter
225 * @param fds the file stream to send the output
226 *
227 * This function generates a pseduo filter code representation of the given
228 * syscall filter and writes it to the given output stream.
229 *
230 */
231static void _gen_pfc_syscall(const struct arch_def *arch,
232 const struct db_sys_list *sys, FILE *fds)
233{
234 unsigned int sys_num = sys->num;
235 const char *sys_name = arch_syscall_resolve_num(arch, sys_num);
236
237 _indent(fds, 1);
238 fprintf(fds, "# filter for syscall \"%s\" (%d) [priority: %d]\n",
239 (sys_name ? sys_name : "UNKNOWN"), sys_num, sys->priority);
240 _indent(fds, 1);
241 fprintf(fds, "if ($syscall == %d)\n", sys_num);
242 if (sys->chains == NULL) {
243 _indent(fds, 2);
244 _pfc_action(fds, sys->action);
245 } else
246 _gen_pfc_chain(arch, sys->chains, 2, fds);
247}
248
249/**
250 * Generate pseudo filter code for an architecture
251 * @param col the seccomp filter collection
252 * @param db the single seccomp filter
253 * @param fds the file stream to send the output
254 *
255 * This function generates a pseudo filter code representation of the given
256 * filter DB and writes it to the given output stream. Returns zero on
257 * success, negative values on failure.
258 *
259 */
260static int _gen_pfc_arch(const struct db_filter_col *col,
261 const struct db_filter *db, FILE *fds)
262{
263 int rc = 0;
264 struct db_sys_list *s_iter;
265 struct pfc_sys_list *p_iter = NULL, *p_new, *p_head = NULL, *p_prev;
266
267 /* sort the syscall list */
268 db_list_foreach(s_iter, db->syscalls) {
269 p_new = malloc(sizeof(*p_new));
270 if (p_new == NULL) {
271 rc = -ENOMEM;
272 goto arch_return;
273 }
274 memset(p_new, 0, sizeof(*p_new));
275 p_new->sys = s_iter;
276
277 p_prev = NULL;
278 p_iter = p_head;
279 while (p_iter != NULL &&
280 s_iter->priority < p_iter->sys->priority) {
281 p_prev = p_iter;
282 p_iter = p_iter->next;
283 }
284 if (p_head == NULL)
285 p_head = p_new;
286 else if (p_prev == NULL) {
287 p_new->next = p_head;
288 p_head = p_new;
289 } else {
290 p_new->next = p_iter;
291 p_prev->next = p_new;
292 }
293 }
294
295 fprintf(fds, "# filter for arch %s (%u)\n",
296 _pfc_arch(db->arch), db->arch->token_bpf);
297 fprintf(fds, "if ($arch == %u)\n", db->arch->token_bpf);
298 p_iter = p_head;
299 while (p_iter != NULL) {
300 if (!p_iter->sys->valid)
301 continue;
302 _gen_pfc_syscall(db->arch, p_iter->sys, fds);
303 p_iter = p_iter->next;
304 }
305 _indent(fds, 1);
306 fprintf(fds, "# default action\n");
307 _indent(fds, 1);
308 _pfc_action(fds, col->attr.act_default);
309
310arch_return:
311 while (p_head != NULL) {
312 p_iter = p_head;
313 p_head = p_head->next;
314 free(p_iter);
315 }
316 return rc;
317}
318
319/**
320 * Generate a pseudo filter code string representation
321 * @param col the seccomp filter collection
322 * @param fd the fd to send the output
323 *
324 * This function generates a pseudo filter code representation of the given
325 * filter collection and writes it to the given fd. Returns zero on success,
326 * negative values on failure.
327 *
328 */
329int gen_pfc_generate(const struct db_filter_col *col, int fd)
330{
331 int rc = 0;
332 int newfd;
333 unsigned int iter;
334 FILE *fds;
335
336 newfd = dup(fd);
337 if (newfd < 0)
338 return errno;
339 fds = fdopen(newfd, "a");
340 if (fds == NULL) {
341 close(newfd);
342 return errno;
343 }
344
345 /* generate the pfc */
346 fprintf(fds, "#\n");
347 fprintf(fds, "# pseudo filter code start\n");
348 fprintf(fds, "#\n");
349
350 for (iter = 0; iter < col->filter_cnt; iter++)
351 _gen_pfc_arch(col, col->filters[iter], fds);
352
353 fprintf(fds, "# invalid architecture action\n");
354 _pfc_action(fds, col->attr.act_badarch);
355 fprintf(fds, "#\n");
356 fprintf(fds, "# pseudo filter code end\n");
357 fprintf(fds, "#\n");
358
359 fflush(fds);
360 fclose(fds);
361
362 return rc;
363}
This page took 0.02911 seconds and 4 git commands to generate.