move alloc_shared to utils
[trinity.git] / tables.c
blob560b89934f2d4fd1bf270b682bf2dace3de8fa45
1 /*
2 * Functions for handling the system call tables.
3 */
5 #include <string.h>
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <stdlib.h>
10 #include "arch.h"
11 #include "arch-syscalls.h"
12 #include "log.h"
13 #include "params.h"
14 #include "syscall.h"
15 #include "shm.h"
16 #include "tables.h"
17 #include "utils.h" // ARRAY_SIZE
19 unsigned long syscalls_todo = 0;
21 bool biarch = FALSE;
23 int search_syscall_table(const struct syscalltable *table, unsigned int nr_syscalls, const char *arg)
25 unsigned int i;
27 /* search by name */
28 for (i = 0; i < nr_syscalls; i++) {
29 if (strcmp(arg, table[i].entry->name) == 0) {
30 //debugf("Found %s at %u\n", table[i].entry->name, i);
31 return i;
35 return -1;
38 void validate_specific_syscall(const struct syscalltable *table, int call)
40 struct syscallentry *entry;
42 if (call == -1)
43 return;
45 entry = table[call].entry;
47 if (entry->flags & AVOID_SYSCALL)
48 output(0, "%s is marked as AVOID. Skipping\n", entry->name);
50 if (entry->flags & NI_SYSCALL)
51 output(0, "%s is NI_SYSCALL. Skipping\n", entry->name);
54 int validate_specific_syscall_silent(const struct syscalltable *table, int call)
56 struct syscallentry *entry;
58 if (call == -1)
59 return FALSE;
61 entry = table[call].entry;
63 if (entry->flags & AVOID_SYSCALL)
64 return FALSE;
66 if (entry->flags & NI_SYSCALL)
67 return FALSE;
69 return TRUE;
72 void activate_syscall_in_table(unsigned int calln, unsigned int *nr_active, const struct syscalltable *table, int *active_syscall)
74 struct syscallentry *entry = table[calln].entry;
76 //Check if the call is activated already, and activate it only if needed
77 if (entry->active_number == 0) {
78 //Sanity check
79 if ((*nr_active + 1) > MAX_NR_SYSCALL) {
80 output(0, "[tables] MAX_NR_SYSCALL needs to be increased. More syscalls than active table can fit.\n");
81 exit(EXIT_FAILURE);
84 //save the call no
85 active_syscall[*nr_active] = calln + 1;
86 (*nr_active) += 1;
87 entry->active_number = *nr_active;
91 void deactivate_syscall_in_table(unsigned int calln, unsigned int *nr_active, const struct syscalltable *table, int *active_syscall)
93 struct syscallentry *entry;
95 entry = table[calln].entry;
97 //Check if the call is activated already, and deactivate it only if needed
98 if ((entry->active_number != 0) && (*nr_active > 0)) {
99 unsigned int i;
101 for (i = entry->active_number - 1; i < *nr_active - 1; i++) {
102 active_syscall[i] = active_syscall[i + 1];
103 table[active_syscall[i] - 1].entry->active_number = i + 1;
105 //The last step is to erase the last item.
106 active_syscall[*nr_active - 1] = 0;
107 (*nr_active) -= 1;
108 entry->active_number = 0;
112 void count_syscalls_enabled(void)
114 if (biarch == TRUE) {
115 output(0, "32-bit syscalls: %d enabled, %d disabled. "
116 "64-bit syscalls: %d enabled, %d disabled.\n",
117 shm->nr_active_32bit_syscalls, max_nr_32bit_syscalls - shm->nr_active_32bit_syscalls,
118 shm->nr_active_64bit_syscalls, max_nr_64bit_syscalls - shm->nr_active_64bit_syscalls);
119 } else {
120 output(0, "Enabled %d syscalls. Disabled %d syscalls.\n",
121 shm->nr_active_syscalls, max_nr_syscalls - shm->nr_active_syscalls);
125 void init_syscalls(void)
127 if (biarch == TRUE)
128 init_syscalls_biarch();
129 else
130 init_syscalls_uniarch();
133 bool no_syscalls_enabled(void)
135 if (biarch == TRUE) {
136 if ((shm->nr_active_32bit_syscalls == 0) && (shm->nr_active_64bit_syscalls == 0))
137 return TRUE;
138 else
139 return FALSE;
142 /* non-biarch */
143 if (shm->nr_active_syscalls == 0)
144 return TRUE;
145 else
146 return FALSE;
149 /* Make sure there's at least one syscall enabled. */
150 int validate_syscall_tables(void)
152 if (biarch == TRUE) {
153 unsigned int ret;
155 ret = validate_syscall_table_32();
156 ret |= validate_syscall_table_64();
157 return ret;
160 /* non-biarch case*/
161 if (shm->nr_active_syscalls == 0)
162 return FALSE;
163 else
164 return TRUE;
167 static void check_syscall(struct syscallentry *entry)
169 /* check that we have a name set. */
170 #define CHECK(NUMARGS, ARGNUM, ARGTYPE, ARGNAME) \
171 if (entry->num_args > 0) { \
172 if (entry->num_args > NUMARGS) { \
173 if (entry->ARGNAME == NULL) { \
174 outputerr("arg %d of %s has no name\n", ARGNUM, entry->name); \
175 exit(EXIT_FAILURE); \
180 CHECK(0, 1, arg1type, arg1name);
181 CHECK(1, 2, arg2type, arg2name);
182 CHECK(2, 3, arg3type, arg3name);
183 CHECK(3, 4, arg4type, arg4name);
184 CHECK(4, 5, arg5type, arg5name);
185 CHECK(5, 6, arg6type, arg6name);
187 /* check if we have a type. */
188 /* note: not enabled by default, because we haven't annotated everything yet. */
189 #undef CHECK
190 #define CHECK(NUMARGS, ARGNUM, ARGTYPE, ARGNAME) \
191 if (entry->num_args > 0) { \
192 if (entry->num_args > NUMARGS) { \
193 if (entry->ARGTYPE == ARG_UNDEFINED) { \
194 outputerr("%s has an undefined argument type for arg1 (%s)!\n", entry->name, entry->ARGNAME); \
199 /* CHECK(0, 1, arg1type, arg1name);
200 CHECK(1, 2, arg2type, arg2name);
201 CHECK(2, 3, arg3type, arg3name);
202 CHECK(3, 4, arg4type, arg4name);
203 CHECK(4, 5, arg5type, arg5name);
204 CHECK(5, 6, arg6type, arg6name);
208 static void sanity_check(const struct syscalltable *table, unsigned int nr)
210 unsigned int i;
212 for (i = 0; i < nr; i++)
213 check_syscall(table[i].entry);
216 void sanity_check_tables(void)
218 if (biarch == TRUE) {
219 sanity_check(syscalls_32bit, max_nr_32bit_syscalls);
220 sanity_check(syscalls_64bit, max_nr_64bit_syscalls);
221 return;
224 /* non-biarch case*/
225 sanity_check(syscalls, max_nr_syscalls);
228 void mark_all_syscalls_active(void)
230 outputstd("Marking all syscalls as enabled.\n");
232 if (biarch == TRUE)
233 mark_all_syscalls_active_biarch();
234 else
235 mark_all_syscalls_active_uniarch();
238 void check_user_specified_arch(const char *arg, char **arg_name, bool *only_64bit, bool *only_32bit)
240 //Check if the arch is specified
241 char *arg_arch = strstr(arg,",");
243 if (arg_arch != NULL) {
244 unsigned long size = 0;
246 size = (unsigned long)arg_arch - (unsigned long)arg;
247 *arg_name = malloc(size + 1);
248 if (*arg_name == NULL)
249 exit(EXIT_FAILURE);
250 (*arg_name)[size] = 0;
251 memcpy(*arg_name, arg, size);
253 //identify architecture
254 if ((only_64bit != NULL) && (only_32bit != NULL)) {
255 if ((strcmp(arg_arch + 1, "64") == 0)) {
256 *only_64bit = TRUE;
257 *only_32bit = FALSE;
258 } else if ((strcmp(arg_arch + 1,"32") == 0)) {
259 *only_64bit = FALSE;
260 *only_32bit = TRUE;
261 } else {
262 outputerr("Unknown bit width (%s). Choose 32, or 64.\n", arg);
263 exit(EXIT_FAILURE);
266 } else {
267 *arg_name = (char*)arg;//castaway const.
271 void clear_check_user_specified_arch(const char *arg, char **arg_name)
273 //Release memory only if we have allocated it
274 if (((char *)arg) != *arg_name) {
275 free(*arg_name);
276 *arg_name = NULL;
280 void toggle_syscall(const char *arg, bool state)
282 int specific_syscall = 0;
283 char * arg_name = NULL;
285 if (biarch == TRUE) {
286 toggle_syscall_biarch(arg, state);
287 return;
290 /* non-biarch case. */
291 check_user_specified_arch(arg, &arg_name, NULL, NULL); //We do not care about arch here, just to get rid of arg flags.
293 specific_syscall = search_syscall_table(syscalls, max_nr_syscalls, arg_name);
294 if (specific_syscall == -1) {
295 outputerr("No idea what syscall (%s) is.\n", arg);
296 goto out;
299 toggle_syscall_n(specific_syscall, state, arg, arg_name);
301 out:
302 clear_check_user_specified_arch(arg, &arg_name);
305 void deactivate_disabled_syscalls(void)
307 output(0, "Disabling syscalls marked as disabled by command line options\n");
309 if (biarch == TRUE)
310 deactivate_disabled_syscalls_biarch();
311 else
312 deactivate_disabled_syscalls_uniarch();
315 void show_state(unsigned int state)
317 if (state)
318 outputstd("Enabled");
319 else
320 outputstd("Disabled");
323 void dump_syscall_tables(void)
325 if (biarch == TRUE)
326 dump_syscall_tables_biarch();
327 else
328 dump_syscall_tables_uniarch();
332 * This changes the pointers in the table 'from' to be copies in
333 * shared mmaps across all children. We do this so that a child can
334 * modify the flags field (adding AVOID for eg) and have other processes see the change.
336 static struct syscalltable * copy_syscall_table(struct syscalltable *from, unsigned int nr)
338 unsigned int n;
339 struct syscallentry *copy;
341 copy = alloc_shared(nr * sizeof(struct syscallentry));
342 if (copy == NULL)
343 exit(EXIT_FAILURE);
345 for (n = 0; n < nr; n++) {
346 memcpy(copy + n , from[n].entry, sizeof(struct syscallentry));
347 copy[n].number = n;
348 copy[n].active_number = 0;
349 from[n].entry = &copy[n];
351 return from;
354 void select_syscall_tables(void)
356 #ifdef ARCH_IS_BIARCH
357 syscalls_64bit = copy_syscall_table(SYSCALLS64, ARRAY_SIZE(SYSCALLS64));
358 syscalls_32bit = copy_syscall_table(SYSCALLS32, ARRAY_SIZE(SYSCALLS32));
360 max_nr_64bit_syscalls = ARRAY_SIZE(SYSCALLS64);
361 max_nr_32bit_syscalls = ARRAY_SIZE(SYSCALLS32);
362 biarch = TRUE;
363 #else
364 syscalls = copy_syscall_table(SYSCALLS, ARRAY_SIZE(SYSCALLS));
365 max_nr_syscalls = ARRAY_SIZE(SYSCALLS);
366 #endif
369 int setup_syscall_group(unsigned int group)
371 if (biarch == TRUE)
372 return setup_syscall_group_biarch(group);
373 else
374 return setup_syscall_group_uniarch(group);
377 const char * print_syscall_name(unsigned int callno, bool is32bit)
379 const struct syscalltable *table;
380 unsigned int max;
382 if (biarch == FALSE) {
383 max = max_nr_syscalls;
384 table = syscalls;
385 } else {
386 if (is32bit == FALSE) {
387 max = max_nr_64bit_syscalls;
388 table = syscalls_64bit;
389 } else {
390 max = max_nr_32bit_syscalls;
391 table = syscalls_32bit;
395 if (callno >= max) {
396 outputstd("Bogus syscall number in %s (%u)\n", __func__, callno);
397 return "invalid-syscall";
400 return table[callno].entry->name;
403 void display_enabled_syscalls(void)
405 if (biarch == TRUE)
406 display_enabled_syscalls_biarch();
407 else
408 display_enabled_syscalls_uniarch();
411 static bool check_for_argtype(const struct syscalltable *table, unsigned int num, unsigned int argtype)
413 struct syscallentry *entry = table[num].entry;
415 unsigned int i;
417 for (i = 0; i < entry->num_args; i++) {
418 switch (i) {
419 case 0: if (entry->arg1type == argtype)
420 return TRUE;
421 break;
422 case 1: if (entry->arg2type == argtype)
423 return TRUE;
424 break;
425 case 2: if (entry->arg3type == argtype)
426 return TRUE;
427 break;
428 case 3: if (entry->arg4type == argtype)
429 return TRUE;
430 break;
431 case 4: if (entry->arg5type == argtype)
432 return TRUE;
433 break;
434 case 5: if (entry->arg6type == argtype)
435 return TRUE;
436 break;
440 return FALSE;
443 /* Consider anything with an ARG_FD or ARG_SOCKADDR a network syscall. */
444 bool is_syscall_net_related(const struct syscalltable *table, unsigned int num)
446 if (check_for_argtype(table, num, ARG_FD) == TRUE)
447 return TRUE;
449 if (check_for_argtype(table, num, ARG_SOCKADDR) == TRUE)
450 return TRUE;
452 return FALSE;
455 void disable_non_net_syscalls(void)
457 output(0, "Disabling non networking related syscalls\n");
459 if (biarch == TRUE)
460 disable_non_net_syscalls_biarch();
461 else
462 disable_non_net_syscalls_uniarch();
464 deactivate_disabled_syscalls();
467 void enable_random_syscalls(void)
469 unsigned int i;
471 if (random_selection_num == 0) {
472 outputerr("-r 0 syscalls ? what?\n");
473 exit(EXIT_FAILURE);
476 if (biarch == TRUE) {
477 if ((random_selection_num > max_nr_64bit_syscalls) && do_64_arch) {
478 outputerr("-r val %d out of range (1-%d)\n", random_selection_num, max_nr_64bit_syscalls);
479 exit(EXIT_FAILURE);
481 } else {
482 if (random_selection_num > max_nr_syscalls) {
483 outputerr("-r val %d out of range (1-%d)\n", random_selection_num, max_nr_syscalls);
484 exit(EXIT_FAILURE);
488 outputerr("Enabling %d random syscalls\n", random_selection_num);
490 for (i = 0; i < random_selection_num; i++) {
491 if (biarch == TRUE)
492 enable_random_syscalls_biarch();
493 else
494 enable_random_syscalls_uniarch();
499 /* This is run *after* we've parsed params */
500 int munge_tables(void)
502 /* By default, all syscall entries will be disabled.
503 * If we didn't pass -c, -x or -r, mark all syscalls active.
505 if ((do_specific_syscall == FALSE) && (do_exclude_syscall == FALSE) && (random_selection == FALSE) && (desired_group == GROUP_NONE))
506 mark_all_syscalls_active();
508 if (desired_group != GROUP_NONE) {
509 unsigned int ret;
511 ret = setup_syscall_group(desired_group);
512 if (ret == FALSE)
513 return FALSE;
516 if (random_selection == TRUE)
517 enable_random_syscalls();
519 /* If we saw a '-x', set all syscalls to enabled, then selectively disable.
520 * Unless we've started enabling them already (with -r) (or if we specified a group -g)
522 if (do_exclude_syscall == TRUE) {
523 if ((random_selection == FALSE) && (desired_group == GROUP_NONE))
524 mark_all_syscalls_active();
525 deactivate_disabled_syscalls();
528 /* if we passed -n, make sure there's no VM/VFS syscalls enabled. */
529 if (no_files == TRUE)
530 disable_non_net_syscalls();
532 sanity_check_tables();
534 count_syscalls_enabled();
536 if (verbose == TRUE)
537 display_enabled_syscalls();
539 if (validate_syscall_tables() == FALSE) {
540 outputstd("No syscalls were enabled!\n");
541 outputstd("Use 32bit:%d 64bit:%d\n", use_32bit, use_64bit);
542 return FALSE;
545 return TRUE;