#!/bin/bash # # libseccomp syscall validation script # # Copyright (c) 2014 Red Hat # Copyright (c) 2020 Cisco Systems, Inc. # # Author: Paul Moore # # # This library is free software; you can redistribute it and/or modify it # under the terms of version 2.1 of the GNU Lesser General Public License as # published by the Free Software Foundation. # # This library is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License # for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this library; if not, see . # LIB_SYS_DUMP="./arch-syscall-dump" #### # functions # # Dependency check # # Arguments: # 1 Dependency to check for # function check_deps() { [[ -z "$1" ]] && return which "$1" >& /dev/null return $? } # # Dependency verification # # Arguments: # 1 Dependency to check for # function verify_deps() { [[ -z "$1" ]] && return if ! check_deps "$1"; then echo "error: install \"$1\" and include it in your \$PATH" exit 1 fi } # # Print out script usage details # function usage() { cat << EOF usage: arch-syscall-validate [-h] [-c] [-a ] libseccomp syscall validation script optional arguments: -h show this help message and exit -a architecture -l output the library's syscall definitions -s output the kernel's syscall definitions -c generate a CSV of the kernel's syscall definitions EOF } # # Dump the kernel version # # Arguments: # 1 path to the kernel source # # Dump the kernel's version information to stdout. # function kernel_version() { local maj=$(cat $1/Makefile | \ grep "^VERSION =" | awk -F "= " '{ print $2 }') local min=$(cat $1/Makefile | \ grep "^PATCHLEVEL =" | awk -F "= " '{ print $2 }') local sub=$(cat $1/Makefile | \ grep "^SUBLEVEL =" | awk -F "= " '{ print $2 }') local xtr=$(cat $1/Makefile | \ grep "^EXTRAVERSION =" | awk -F "= " '{ print $2 }') echo "${maj}.${min}.${sub}${xtr}" } # # Dump the library syscall table for a given architecture # # Arguments: # 1 architecture # 2 offset (optional) # # # Dump the library's syscall table to stdout. # function dump_lib_arch() { local offset_str="" [[ -z $1 ]] && return [[ -n $2 ]] && offset_str="-o $2" $LIB_SYS_DUMP -a $1 $offset_str | sed 's/\t/,/' | sort } # # Mangle the library pseudo syscall values # # Arguments: # 1 architecture # # Mangle the supplied pseudo syscall to match the system values # function mangle_lib_syscall() { local sed_filter="" sed_filter+='s/accept4,-118/accept4,364/;' sed_filter+='s/bind,-102/bind,361/;' sed_filter+='s/connect,-103/connect,362/;' sed_filter+='s/getpeername,-107/getpeername,368/;' sed_filter+='s/getsockname,-106/getsockname,367/;' sed_filter+='s/getsockopt,-115/getsockopt,365/;' sed_filter+='s/listen,-104/listen,363/;' sed_filter+='s/msgctl,-214/msgctl,402/;' sed_filter+='s/msgget,-213/msgget,399/;' sed_filter+='s/msgrcv,-212/msgrcv,401/;' sed_filter+='s/msgsnd,-211/msgsnd,400/;' sed_filter+='s/recvfrom,-112/recvfrom,371/;' sed_filter+='s/recvmsg,-117/recvmsg,372/;' sed_filter+='s/semctl,-203/semctl,394/;' sed_filter+='s/semget,-202/semget,393/;' sed_filter+='s/sendmsg,-116/sendmsg,370/;' sed_filter+='s/sendto,-111/sendto,369/;' sed_filter+='s/setsockopt,-114/setsockopt,366/;' sed_filter+='s/shmat,-221/shmat,397/;' sed_filter+='s/shmctl,-224/shmctl,396/;' sed_filter+='s/shmdt,-222/shmdt,398/;' sed_filter+='s/shmget,-223/shmget,395/;' sed_filter+='s/shutdown,-113/shutdown,373/;' sed_filter+='s/socket,-101/socket,359/;' sed_filter+='s/socketpair,-108/socketpair,360/;' case $1 in s390|s390x) sed_filter+='s/recvmmsg,-119/recvmmsg,357/;' sed_filter+='s/sendmmsg,-120/sendmmsg,358/;' ;; *) sed_filter+='s/recvmmsg,-119/recvmmsg,337/;' sed_filter+='s/sendmmsg,-120/sendmmsg,345/;' ;; esac sed $sed_filter | sed '/,-[0-9]\+$/d' } # # Dump the x86 system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_x86() { cat $1/arch/x86/entry/syscalls/syscall_32.tbl | \ grep -v "^#" | awk '{ print $3","$1 }' | \ sort } # # Dump the x86 library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_x86() { dump_lib_arch x86 | mangle_lib_syscall x86 } # # Dump the x86_64 system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_x86_64() { cat $1/arch/x86/entry/syscalls/syscall_64.tbl | \ grep -v "^#" | sed '/^$/d' | awk '{ print $2,$3,$1 }' | \ sed '/^x32/d' | awk '{ print $2","$3 }' | sort } # # Dump the x86_64 library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_x86_64() { dump_lib_arch x86_64 | mangle_lib_syscall x86_64 } # # Dump the x32 system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_x32() { cat $1/arch/x86/entry/syscalls/syscall_64.tbl | \ grep -v "^#" | sed '/^$/d' | awk '{ print $2,$3,$1 }' | \ sed '/^64/d' | awk '{ print $2","$3 }' | sort } # # Dump the x32 library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_x32() { dump_lib_arch x32 | mangle_lib_syscall x32 } # # Dump the arm system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_arm() { cat $1/arch/arm/tools/syscall.tbl | grep -v "^#" | \ sed -n "/[0-9]\+[ \t]\+\(common\|eabi\)/p" | \ awk '{ print $3","$1 }' | sort | (cat -; \ (cat $1/arch/arm/include/uapi/asm/unistd.h | \ grep "^#define __ARM_NR_" | \ grep -v "^#define __ARM_NR_BASE" | \ sed 's/#define __ARM_NR_\([a-z0-9_]*\)[ \t]\+(__ARM_NR_BASE+\(.*\))/\1 983040 + \2/' | \ awk '{ print $1","$2+$4 }')) | sort } # # Dump the arm library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_arm() { # NOTE: arm_sync_file_range() and sync_file_range2() share values dump_lib_arch arm | sed '/sync_file_range2,\+341/d' | \ mangle_lib_syscall arm } # # Dump the aarch64 system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_aarch64() { local sed_filter="" sed_filter+='s/__NR3264_statfs/43/;' sed_filter+='s/__NR3264_ftruncate/46/;' sed_filter+='s/__NR3264_truncate/45/;' sed_filter+='s/__NR3264_lseek/62/;' sed_filter+='s/__NR3264_sendfile/71/;' sed_filter+='s/__NR3264_fstatat/79/;' sed_filter+='s/__NR3264_fstatfs/44/;' sed_filter+='s/__NR3264_fcntl/25/;' sed_filter+='s/__NR3264_fadvise64/223/;' sed_filter+='s/__NR3264_mmap/222/;' sed_filter+='s/__NR3264_fstat/80/;' sed_filter+='s/__NR3264_lstat/1039/;' sed_filter+='s/__NR3264_stat/1038/;' gcc -E -dM -I$1/include/uapi \ -D__BITS_PER_LONG=64 -D__ARCH_WANT_RENAMEAT \ -D__ARCH_WANT_NEW_STAT \ $1/arch/arm64/include/uapi/asm/unistd.h | \ grep "^#define __NR_" | \ sed '/__NR_syscalls/d' | \ sed '/__NR_arch_specific_syscall/d' | \ sed 's/#define[ \t]\+__NR_\([^ \t]\+\)[ \t]\+\(.*\)/\1,\2/' | \ sed $sed_filter | sort } # # Dump the aarch64 library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_aarch64() { dump_lib_arch aarch64 | mangle_lib_syscall aarch64 } # # Dump the mips system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_mips() { cat $1/arch/mips/kernel/syscalls/syscall_o32.tbl | \ grep -v "^#" | \ sed -e '/[ \t]\+reserved[0-9]\+[ \t]\+/d;' | \ sed -e '/[ \t]\+unused[0-9]\+[ \t]\+/d;' | \ awk '{ print $3","$1 }' | sort } # # Dump the mips library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_mips() { dump_lib_arch mips | mangle_lib_syscall mips } # # Dump the mips64 system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_mips64() { cat $1/arch/mips/kernel/syscalls/syscall_n64.tbl | \ grep -v "^#" | \ sed -e '/[ \t]\+reserved[0-9]\+[ \t]\+/d;' | \ sed -e '/[ \t]\+unused[0-9]\+[ \t]\+/d;' | \ awk '{ print $3","$1 }' | sort } # # Dump the mips64 library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_mips64() { dump_lib_arch mips64 | mangle_lib_syscall mips64 } # # Dump the mips64n32 system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_mips64n32() { cat $1/arch/mips/kernel/syscalls/syscall_n32.tbl | \ grep -v "^#" | \ sed -e '/[ \t]\+reserved[0-9]\+[ \t]\+/d;' | \ sed -e '/[ \t]\+unused[0-9]\+[ \t]\+/d;' | \ awk '{ print $3","$1 }' | sort } # # Dump the mips64n32 library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_mips64n32() { dump_lib_arch mips64n32 | mangle_lib_syscall mips64n32 } # # Dump the parisc system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_parisc() { cat $1/arch/parisc/kernel/syscalls/syscall.tbl | \ grep -v "^#" | \ sed -n "/[0-9]\+[ \t]\+\(common\|32\)/p" | \ awk '{ print $3","$1 }' | \ sort } # # Dump the parisc library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_parisc() { dump_lib_arch parisc | mangle_lib_syscall parisc } # # Dump the parisc64 system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_parisc64() { cat $1/arch/parisc/kernel/syscalls/syscall.tbl | \ grep -v "^#" | \ sed -n "/[0-9]\+[ \t]\+\(common\|64\)/p" | \ awk '{ print $3","$1 }' | \ sort } # # Dump the parisc64 library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_parisc64() { dump_lib_arch parisc64 | mangle_lib_syscall parisc64 } # # Dump the ppc system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_ppc() { cat $1/arch/powerpc/kernel/syscalls/syscall.tbl | grep -v "^#" | \ sed -ne "/[0-9]\+[ \t]\+\(common\|nospu\|32\)/p" | \ awk '{ print $3","$1 }' | sort } # # Dump the ppc library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_ppc() { dump_lib_arch ppc | mangle_lib_syscall ppc } # # Dump the ppc64 system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_ppc64() { cat $1/arch/powerpc/kernel/syscalls/syscall.tbl | grep -v "^#" | \ sed -ne "/[0-9]\+[ \t]\+\(common\|nospu\|64\)/p" | \ awk '{ print $3","$1 }' | sort } # # Dump the ppc64 library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_ppc64() { dump_lib_arch ppc64 | mangle_lib_syscall ppc64 } # # Dump the riscv64 system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_riscv64() { local sed_filter="" sed_filter+='s/__NR3264_fadvise64/223/;' sed_filter+='s/__NR3264_fcntl/25/;' sed_filter+='s/__NR3264_fstatat/79/;' sed_filter+='s/__NR3264_fstatfs/44/;' sed_filter+='s/__NR3264_ftruncate/46/;' sed_filter+='s/__NR3264_lseek/62/;' sed_filter+='s/__NR3264_mmap/222/;' sed_filter+='s/__NR3264_sendfile/71/;' sed_filter+='s/__NR3264_statfs/43/;' sed_filter+='s/__NR3264_truncate/45/;' sed_filter+='s/__NR3264_fstat/80/;' gcc -E -dM -I$1/include/uapi \ -D__BITS_PER_LONG=64 -D__ARCH_WANT_NEW_STAT \ $1/arch/riscv/include/uapi/asm/unistd.h | \ grep "^#define __NR_" | \ sed '/__NR_syscalls/d' | \ sed 's/(__NR_arch_specific_syscall + 15)/259/' | \ sed '/__NR_arch_specific_syscall/d' | \ sed 's/#define[ \t]\+__NR_\([^ \t]\+\)[ \t]\+\(.*\)/\1,\2/' | \ sed $sed_filter | sort } # # Dump the riscv64 library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_riscv64() { dump_lib_arch riscv64 | mangle_lib_syscall riscv64 } # # Dump the s390 system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_s390() { cat $1/arch/s390/kernel/syscalls/syscall.tbl | grep -v "^#" | \ sed -ne "/[0-9]\+[ \t]\+\(common\|32\)/p" | \ awk '{ print $3","$1 }' | \ sort } # # Dump the s390 library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_s390() { dump_lib_arch s390 | mangle_lib_syscall s390 } # # Dump the s390x system syscall table # # Arguments: # 1 path to the kernel source # # Dump the architecture's syscall table to stdout. # function dump_sys_s390x() { cat $1/arch/s390/kernel/syscalls/syscall.tbl | grep -v "^#" | \ sed -ne "/[0-9]\+[ \t]\+\(common\|64\)/p" | \ awk '{ print $3","$1 }' | \ sort } # # Dump the s390x library syscall table # # Dump the library's syscall table to stdout. # function dump_lib_s390x() { dump_lib_arch s390x | mangle_lib_syscall s390x } # # Dump the system syscall table # # Arguments: # 1 architecture # 2 path to the kernel source # # Dump the system's syscall table to stdout using the given architecture. # function dump_sys() { case $1 in x86) dump_sys_x86 "$2" ;; x86_64) dump_sys_x86_64 "$2" ;; x32) dump_sys_x32 "$2" ;; arm) dump_sys_arm "$2" ;; aarch64) dump_sys_aarch64 "$2" ;; mips) dump_sys_mips "$2" ;; mips64) dump_sys_mips64 "$2" ;; mips64n32) dump_sys_mips64n32 "$2" ;; parisc) dump_sys_parisc "$2" ;; parisc64) dump_sys_parisc64 "$2" ;; ppc) dump_sys_ppc "$2" ;; ppc64) dump_sys_ppc64 "$2" ;; riscv64) dump_sys_riscv64 "$2" ;; s390) dump_sys_s390 "$2" ;; s390x) dump_sys_s390x "$2" ;; *) echo "" return 1 ;; esac return 0 } # # Dump the library syscall table # # Arguments: # 1 architecture # # Dump the library's syscall table to stdout using the given architecture. # function dump_lib() { case $1 in x86) dump_lib_x86 ;; x86_64) dump_lib_x86_64 ;; x32) dump_lib_x32 ;; arm) dump_lib_arm ;; aarch64) dump_lib_aarch64 ;; mips) dump_lib_mips ;; mips64) dump_lib_mips64 ;; mips64n32) dump_lib_mips64n32 ;; parisc) dump_lib_parisc ;; parisc64) dump_lib_parisc64 ;; ppc) dump_lib_ppc ;; ppc64) dump_lib_ppc64 ;; riscv64) dump_lib_riscv64 ;; s390) dump_lib_s390 ;; s390x) dump_lib_s390x ;; *) echo "" return 1 ;; esac return 0 } # # Generate the syscall csv file # # Arguments: # 1 path to the kernel source # 2 "sys" or "lib" depending on the syscall list to use # # Generare a syscall csv file from the given kernel sources. # function gen_csv() { # sanity checks [[ -z $1 ]] && return [[ $2 != "sys" && $2 != "lib" ]] && return # abi list # NOTE: the ordering here is dependent on the layout of the # arch_syscall_table struct in syscalls.h - BEWARE! abi_list="" abi_list+=" x86 x86_64 x32" abi_list+=" arm aarch64" abi_list+=" mips mips64 mips64n32" abi_list+=" parisc parisc64" abi_list+=" ppc ppc64" abi_list+=" riscv64" abi_list+=" s390 s390x" # get the full syscall list for abi in $abi_list; do eval output_$abi=$(mktemp -t syscall_validate_XXXXXX) dump_$2_$abi "$1" > $(eval echo $`eval echo output_$abi`) done sc_list=$((for abi in $abi_list; do cat $(eval echo $`eval echo output_$abi`); done) | awk -F "," '{ print $1 }' | sort -u) # output a simple header printf "#syscall (v%s %s)" \ "$(kernel_version "$1")" "$(TZ=UTC date "+%Y-%m-%d")" for abi in $abi_list; do printf ",%s" $abi done printf "\n" # output the syscall csv details for sc in $sc_list; do printf "%s" $sc for abi in $abi_list; do num=$(grep "^$sc," \ $(eval echo $`eval echo output_$abi`) | \ awk -F "," '{ print $2 }' ) [[ -z $num ]] && num="PNR" printf ",%s" $num done printf "\n" done # cleanup for abi in $abi_list; do rm -f $(eval echo $`eval echo output_$abi`) done } #### # main verify_deps diff verify_deps gcc verify_deps grep verify_deps mktemp verify_deps sed if [[ ! -x $LIB_SYS_DUMP ]]; then echo "error: \"$LIB_SYS_DUMP\" is not in the current working directory" exit 1 fi opt_arches="" opt_csv=0 opt_sys=0 opt_lib=0 while getopts "a:cslh" opt; do case $opt in a) opt_arches+="$OPTARG " ;; c) opt_csv=1 ;; s) opt_sys=1 opt_lib=0 ;; l) opt_sys=0 opt_lib=1 ;; h|*) usage exit 1 ;; esac done shift $(($OPTIND - 1)) # defaults if [[ $opt_arches == "" ]]; then opt_arches=" \ x86 x86_64 x32 \ arm aarch64 \ mips mips64 mips64n32 \ parisc parisc64 \ ppc ppc64 \ s390 s390x" fi # sanity checks kernel_dir="$1" if [[ -z $kernel_dir ]]; then usage exit 1 fi if [[ ! -d $kernel_dir ]]; then echo "error: \"$1\" is not a valid directory" exit 1 fi # generate some temp files tmp_lib=$(mktemp -t syscall_validate_XXXXXX) tmp_sys=$(mktemp -t syscall_validate_XXXXXX) if [[ $opt_csv -eq 1 ]]; then # generate the syscall csv file if [[ $opt_lib -eq 1 ]]; then gen_csv $kernel_dir "lib" else gen_csv $kernel_dir "sys" fi else # loop through the architectures and compare for i in $opt_arches; do # dump the syscall tables dump_lib $i > $tmp_lib if [[ $? -ne 0 ]]; then echo "error: unknown arch $i" exit 1 fi dump_sys $i "$kernel_dir" > $tmp_sys if [[ $? -ne 0 ]]; then echo "error: unknown arch $i" exit 1 fi if [[ $opt_lib -eq 1 ]]; then cat $tmp_lib elif [[ $opt_sys -eq 1 ]]; then cat $tmp_sys else # compare the lib and sys output diff -u --label="$i [library]" $tmp_lib \ --label "$i [system]" $tmp_sys fi done fi # cleanup and exit rm -f $tmp_lib $tmp_sys exit 0