From 49d2d37856368fe6bae685bdbb13029ad4803762 Mon Sep 17 00:00:00 2001 From: Alice Frosi Date: Wed, 21 Dec 2022 11:06:37 +0100 Subject: Generate syscalls numbers with nr_syscalls.sh Refactor filter.sh script by: * renaming the filter.sh to nr_syscalls.sh * removing the BPF filter generation * simplifying the syscall number and header generation Signed-off-by: Alice Frosi --- Makefile | 4 +- filter.sh | 265 --------------------------------------------------------- nr_syscalls.sh | 63 ++++++++++++++ 3 files changed, 65 insertions(+), 267 deletions(-) delete mode 100755 filter.sh create mode 100755 nr_syscalls.sh diff --git a/Makefile b/Makefile index 1555c5c..c2b600e 100644 --- a/Makefile +++ b/Makefile @@ -34,8 +34,8 @@ seitan: seitan.c transform.h filter.h: qemu_filter ./filter.sh qemu_filter -numbers.h: qemu_filter - ./filter.sh qemu_filter +numbers.h: + ./nr_syscalls.sh transform.h: qemu_filter ./transform.sh qemu_filter diff --git a/filter.sh b/filter.sh deleted file mode 100755 index b3b85a2..0000000 --- a/filter.sh +++ /dev/null @@ -1,265 +0,0 @@ -#!/bin/sh -eu -# -# SPDX-License-Identifier: AGPL-3.0-or-later -# -# SEITAN - Syscall Expressive Interpreter, Transformer and Notifier -# -# filter.sh - Build binary-search tree BPF program with SECCOMP_RET_USER_NOTIF -# -# Copyright (c) 2022 Red Hat GmbH -# Author: Stefano Brivio - -TMP="$(mktemp)" -IN="${@}" -OUT="filter.h" -OUT_NUMBERS="numbers.h" - -HEADER="/* This file was automatically generated by $(basename ${0}) */ - -#ifndef AUDIT_ARCH_PPC64LE -#define AUDIT_ARCH_PPC64LE (AUDIT_ARCH_PPC64 | __AUDIT_ARCH_LE) -#endif" - -HEADER_NUMBERS="/* This file was automatically generated by $(basename ${0}) */ -struct syscall_numbers numbers[] = {" - -FOOTER_NUMBERS="};" - -# Prefix for each profile: check that 'arch' in seccomp_data is matching -PRE=' -struct sock_filter @PROFILE@[] = { - /* cppcheck-suppress badBitmaskCheck */ - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, - (offsetof(struct seccomp_data, arch))), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SEITAN_AUDIT_ARCH, 0, @KILL@), - /* cppcheck-suppress badBitmaskCheck */ - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, - (offsetof(struct seccomp_data, nr))), - -' - -# Suffix for each profile: return actions -POST=' BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_USER_NOTIF), -}; -' - -# Syscall, @NR@: number, @ALLOW@: offset to RET_ALLOW, @NAME@: syscall name -CALL=' BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, @NR@, @ALLOW@, 0), /* @NAME@ */' - -# Binary search tree node or leaf, @NR@: value, @R@: right jump, @L@: left jump -BST=' BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, @NR@, @R@, @L@),' - -# cleanup() - Remove temporary file if it exists -cleanup() { - rm -f "${TMP}" -} -#trap "cleanup" EXIT - -# sub() - Substitute in-place file line with processed template line -# $1: Line number -# $2: Template name (variable name) -# $@: Replacement for @KEY@ in the form KEY:value -sub() { - IFS= - __line_no="${1}" - __template="$(eval printf '%s' "\${${2}}")" - shift; shift - - sed -i "${__line_no}s#.*#${__template}#" "${TMP}" - - IFS=' ' - for __def in ${@}; do - __key="@${__def%%:*}@" - __value="${__def#*:}" - sed -i "${__line_no}s/${__key}/${__value}/" "${TMP}" - done - unset IFS -} - -# finish() - Finalise header file from temporary files with prefix and suffix -# $1: Variable name of prefix -# $@: Replacements for prefix variable -finish() { - IFS= - __out="$(eval printf '%s' "\${${1}}")" - shift - - IFS=' ' - for __def in ${@}; do - __key="@${__def%%:*}@" - __value="${__def#*:}" - __out="$(printf '%s' "${__out}" | sed "s#${__key}#${__value}#")" - done - - printf '%s\n' "${__out}" >> "${OUT}" - cat "${TMP}" >> "${OUT}" - rm "${TMP}" - printf '%s' "${POST}" >> "${OUT}" - unset IFS -} - -# log2() - Binary logarithm -# $1: Operand -log2() { - __x=-1 - __y=${1} - while [ ${__y} -gt 0 ]; do : $((__y >>= 1)); __x=$((__x + 1)); done - echo ${__x} -} - -# syscall_nr - Get syscall number from compiler, also note in numbers.h -# $1: Name of syscall -syscall_nr() { - __in="$(printf "#include \n#include \n__NR_%s" ${1})" - __out="$(echo "${__in}" | cc -E -xc - -o - | tail -1)" - [ "${__out}" = "__NR_$1" ] && return 1 - - # Output might be in the form "(x + y)" (seen on armv6l, armv7l) - __out="$(eval echo $((${__out})))" - echo "${__out}" - - printf "\t{ \"%s\",\t\t%i },\n" "${1}" "${__out}" >> "${OUT_NUMBERS}" -} - -filter() { - __filtered= - for __c in ${@}; do - __arch_match=0 - case ${__c} in - *:*) - case ${__c} in - $(uname -m):*) - __arch_match=1 - __c=${__c##*:} - ;; - esac - ;; - *) - __arch_match=1 - ;; - esac - [ ${__arch_match} -eq 0 ] && continue - - IFS='| ' - __found=0 - for __name in ${__c}; do - syscall_nr "${__name}" >/dev/null && __found=1 && break - done - unset IFS - - if [ ${__found} -eq 0 ]; then - echo - echo "Warning: no syscall number for ${__c}" >&2 - echo " none of these syscalls will be allowed" >&2 - continue - fi - - __filtered="${__filtered} ${__name}" - done - - echo "${__filtered}" | tr ' ' '\n' | sort -u -} - -# gen_profile() - Build struct sock_filter for a single profile -# $1: Profile name -# $@: Names of allowed system calls, amount padded to next power of two -gen_profile() { - __profile="${1}" - shift - - __statements_calls=${#} - __bst_levels=$(log2 $(( __statements_calls / 4 )) ) - __statements_bst=$(( __statements_calls / 4 - 1 )) - __statements=$((__statements_calls + __statements_bst)) - - [ ${__bst_levels} -eq 0 ] && __statements_bst=0 - for __i in $(seq 1 ${__statements_bst} ); do - echo -1 >> "${TMP}" - done - - for __i in $(seq 1 ${__statements_calls} ); do - __syscall_name="$(eval echo \${${__i}})" - if ! syscall_nr ${__syscall_name} >> "${TMP}"; then - echo "Cannot get syscall number for ${__syscall_name}" - exit 1 - fi - eval __syscall_nr_$(tail -1 "${TMP}")="${__syscall_name}" - done - sort -go "${TMP}" "${TMP}" - - __level_nodes=1 - __distance=$(( __statements_calls / 2 )) - __ll=0 - __line=1 - for __level in $(seq 1 $(( __bst_levels - 1 )) ); do - # Nodes - __cmp_pos=${__distance} - - for __node in $(seq 1 ${__level_nodes}); do - __cmp_line=$(( __statements_bst + __cmp_pos )) - __lr=$(( __ll + 1 )) - __nr="$(sed -n ${__cmp_line}p "${TMP}")" - - sub ${__line} BST "NR:${__nr}" "L:${__ll}" "R:${__lr}" - - __ll=${__lr} - __line=$(( __line + 1 )) - __cmp_pos=$(( __cmp_pos + __distance * 2 )) - done - - __distance=$(( __distance / 2 )) - __level_nodes=$(( __level_nodes * 2 )) - done - - # Leaves - if [ ${__bst_levels} -eq 0 ]; then - __ll=0 - else - __ll=$(( __level_nodes - 1 )) - fi - __lr=$(( __ll + __distance - 1 )) - __cmp_pos=${__distance} - - for __leaf in $(seq 1 ${__level_nodes}); do - __cmp_line=$(( __statements_bst + __cmp_pos )) - __nr="$(sed -n ${__cmp_line}p "${TMP}")" - sub ${__line} BST "NR:${__nr}" "L:${__ll}" "R:${__lr}" - - __ll=$(( __lr + __distance - 1 )) - __lr=$(( __ll + __distance)) - __line=$(( __line + 1 )) - __cmp_pos=$(( __cmp_pos + __distance * 2 )) - done - - # Calls - [ ${__bst_levels} -eq 0 ] && __statements_bst=$((__statements_bst + 1)) - for __i in $(seq $(( __statements_bst + 1 )) ${__statements}); do - __nr="$(sed -n ${__i}p "${TMP}")" - eval __name="\${__syscall_nr_${__nr}}" - __allow=$(( __statements - __i + 1)) - sub ${__i} CALL "NR:${__nr}" "NAME:${__name}" "ALLOW:${__allow}" - done - finish PRE "PROFILE:${__profile}" "KILL:$(( __statements + 1))" -} - -printf '%s\n' "${HEADER}" > "${OUT}" -printf '%s\n' "${HEADER_NUMBERS}" > "${OUT_NUMBERS}" -__profiles="${IN}" -for __p in ${__profiles}; do - __calls="$(sed -n 's/^\([^# \t]\{1,\}\).*/\1/p' "${__p}")" - __calls="$(filter ${__calls})" - echo "seccomp profile ${__p} handles: ${__calls}" | tr '\n' ' ' | fmt -t - - # Pad here to keep gen_profile() "simple" - __count=0 - for __c in ${__calls}; do __count=$(( __count + 1 )); done - __padded=$(( 1 << (( $(log2 ${__count}) + 1 )) )) - for __i in $( seq ${__count} $(( __padded - 1 )) ); do - __calls="${__calls} read" - done - - gen_profile "${__p}" ${__calls} -done - -printf '%s\n' "${FOOTER_NUMBERS}" >> "${OUT_NUMBERS}" diff --git a/nr_syscalls.sh b/nr_syscalls.sh new file mode 100755 index 0000000..9ad3d19 --- /dev/null +++ b/nr_syscalls.sh @@ -0,0 +1,63 @@ +#!/bin/sh -eu +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# SEITAN - Syscall Expressive Interpreter, Transformer and Notifier +# +# filter.sh - Build binary-search tree BPF program with SECCOMP_RET_USER_NOTIF +# +# Copyright (c) 2022 Red Hat GmbH +# Author: Stefano Brivio + +IN="${@}" +OUT_NUMBERS="numbers.h" + +HEADER_NUMBERS="/* This file was automatically generated by $(basename ${0}) */ +#ifndef NUMBERS_H_ +#define NUMBERS_H_ + +struct syscall_numbers { + char name[1024]; + long number; +}; + +struct syscall_numbers numbers[] = {" + +FOOTER_NUMBERS="}; + +#endif" + +syscalls=( + "accept" + "bind" + "connect" + "getpeername" + "getsockname" + "getsockopt" + "listen" + "mount" + "openat" + "recvfrom" + "recvmmsg" + "recvmsg" + "sendmmsg" + "sendmsg" + "sendto" + "setsockopt" + "shutdown" + "socket" + "socketpair" +) + +printf '%s\n' "${HEADER_NUMBERS}" > "${OUT_NUMBERS}" +# syscall_nr - Get syscall number from compiler, also note in numbers.h +__in="$(printf "#include \n#include \n__NR_%s" ${syscalls[@]})" +__out="$(echo "${__in}" |cc -E -xc - -o - |sed -n '/\#.*$/!p'| sed '/^$/d')" + +awk -v AS="${syscalls[*]}" -v BS="${__out[*]}" \ +'BEGIN { MAX=split(AS,a, / /); split(BS,b, / /); +for (x = 1; x <= MAX; x++) + { printf "\t{\"%s\",\t%d},\n", a[x], b[x] } + }' >> "${OUT_NUMBERS}" + +printf '%s\n' "${FOOTER_NUMBERS}" >> "${OUT_NUMBERS}" -- cgit v1.2.3