#!/usr/bin/sh
test "$1" = "-h" -o "$1" = "-hh" -o "$1" = "--help" && {
  echo 'afl-system-config by Marc Heuse <mh@mh-sec.de>'
  echo
  echo $0
  echo
  echo afl-system-config has no command line options
  echo
  echo afl-system-config reconfigures the system to a high performance fuzzing state.
  echo "WARNING: this reduces the security of the system!"
  echo
  echo Note that there is also afl-persistent-config which sets additional permanent
  echo configuration options.
  exit 0
}
if [ $# -ne 0 ]; then
  echo "ERROR: Unknown option(s): $@"
  exit 1
fi

DONE=
PLATFORM=`uname -s`
SYSCTL=sysctl
command -v sysctl >/dev/null 2>&1 || SYSCTL=/sbin/sysctl

set_sysctl() {
  SYSCTL_PROC="/proc/sys/$(echo "$1" | tr '.' '/')"
  if test -e "$SYSCTL_PROC" ; then
    CURRENT=$(cat "$SYSCTL_PROC" 2>/dev/null)
  else
    test -d /proc/sys && return 0
    CURRENT=$($SYSCTL -n "$1" 2>/dev/null) || return 0
  fi
  test "$CURRENT" = "$2" && return 0
  $SYSCTL -w "$1=$2" >/dev/null 2>&1
  test -e "$SYSCTL_PROC" && {
    test "$(cat "$SYSCTL_PROC" 2>/dev/null)" = "$2" && return 0
    { echo "$2" > "$SYSCTL_PROC"; } 2>/dev/null && return 0
  }
  test -e "$SYSCTL_PROC" || { test "$($SYSCTL -n "$1" 2>/dev/null)" = "$2" && return 0; }
  test -z "$3" && echo "Warning: failed to set $1 to \"$2\"" >&2
  return 1
}

set_file() {
  test -e "$1" || return 0
  CURRENT=$(cat "$1" 2>/dev/null)
  test "$CURRENT" = "$2" && return 0
  case "$CURRENT" in *"[$2]"*) return 0 ;; esac
  { echo "$2" > "$1"; } 2>/dev/null && return 0
  test -z "$3" && echo "Warning: failed to write \"$2\" to $1" >&2
  return 1
}

echo This reconfigures the system to have a better fuzzing performance.
echo "WARNING: this reduces the security of the system!"
echo
if [ '!' "$EUID" = 0 ] && [ '!' `id -u` = 0 ] ; then
	echo "Warning: you need to be root to run this!"
	sleep 1
	# we do not exit as other mechanisms exist that allows to do this than
	# being root. let the errors speak for themselves.
fi
sleep 1
if [ "$PLATFORM" = "Linux" ] ; then
{
  set_sysctl kernel.core_uses_pid 0
  # Arch Linux requires core_pattern to be empty :(
  test -e /etc/arch-release && set_sysctl kernel.core_pattern ""
  test -e /etc/arch-release || set_sysctl kernel.core_pattern core
  set_sysctl kernel.randomize_va_space 0
  set_sysctl kernel.sched_child_runs_first 1
  set_sysctl kernel.sched_autogroup_enabled 1
  set_sysctl kernel.sched_migration_cost_ns 50000000 optional
  set_sysctl kernel.sched_latency_ns 250000000 optional
  set_sysctl vm.swappiness 10 optional
  set_file /sys/kernel/mm/transparent_hugepage/enabled never
  GOVERNORS_FOUND=0
  GOVERNORS_FAILED=0
  for GOVERNOR in /sys/devices/system/cpu/cpufreq/scaling_governor \
                  /sys/devices/system/cpu/cpufreq/policy*/scaling_governor \
                  /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor ; do
    test -e "$GOVERNOR" || continue
    GOVERNORS_FOUND=$((GOVERNORS_FOUND + 1))
    set_file "$GOVERNOR" performance optional || GOVERNORS_FAILED=$((GOVERNORS_FAILED + 1))
  done
  test "$GOVERNORS_FAILED" -gt 0 && echo "Warning: failed to set the cpu scaling governor to performance ($GOVERNORS_FAILED of $GOVERNORS_FOUND cpufreq entries failed)" >&2
  set_file /sys/devices/system/cpu/intel_pstate/no_turbo 0
  set_file /sys/devices/system/cpu/cpufreq/boost 1
  set_file /sys/devices/system/cpu/intel_pstate/max_perf_pct 100
  test -n "$(which auditctl)" && auditctl -a never,task >/dev/null 2>&1
} > /dev/null
  echo Settings applied.
  echo
  dmesg | grep -E -q 'noibrs pcid nopti' || {
    echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
    echo '  /etc/default/grub:GRUB_CMDLINE_LINUX_DEFAULT="ibpb=off ibrs=off kpti=off l1tf=off spec_rstack_overflow=off mds=off nokaslr no_stf_barrier noibpb noibrs pcid nopti nospec_store_bypass_disable nospectre_v1 nospectre_v2 pcid=on pti=off spec_store_bypass_disable=off spectre_v2=off stf_barrier=off srbds=off noexec=off noexec32=off tsx=on tsx=on tsx_async_abort=off mitigations=off audit=0 hardened_usercopy=off ssbd=force-off"'
    echo
  }
  echo If you run fuzzing instances in docker, run them with \"--security-opt seccomp=unconfined\" for more speed.
  echo
  DONE=1
fi
if [ "$PLATFORM" = "FreeBSD" ] ; then
{
  set_sysctl kern.elf32.aslr.enable 0
  set_sysctl kern.elf64.aslr.enable 0
} > /dev/null
  echo Settings applied.
  echo
  cat <<EOF
In order to suppress core file generation during fuzzing it is recommended to set
me:\\
	:coredumpsize=0:
in the ~/.login_conf file for the user used for fuzzing.
EOF
  echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this:
  echo '  sysctl hw.ibrs_disable=1'
  echo 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.'
  echo
  DONE=1
fi
if [ "$PLATFORM" = "OpenBSD" ] ; then
  test -n "$($SYSCTL -n vm.malloc_conf 2>/dev/null)" && doas sysctl vm.malloc_conf=
  echo 'Freecheck on allocation in particular can be detrimental to performance.'
  echo 'Also we might not want necessarily to abort at any allocation failure.'
  echo 'System security features cannot be disabled on OpenBSD.'
  echo
  DONE=1
fi
if [ "$PLATFORM" = "DragonFly" ] ; then
  #/sbin/sysctl kern.corefile=/dev/null
  #echo Settings applied.
  cat <<EOF
In order to suppress core file generation during fuzzing it is recommended to set
me:\\
	:coredumpsize=0:
in the ~/.login_conf file for the user used for fuzzing.
EOF
  echo
  DONE=1
fi
if [ "$PLATFORM" = "NetBSD" ] ; then
{
  set_sysctl security.models.extensions.user_set_cpu_affinity 1
} > /dev/null
  echo Settings applied.
  echo
  DONE=1
fi
if [ "$PLATFORM" = "Darwin" ] ; then
  set_sysctl kern.sysv.shmmax 524288000
  set_sysctl kern.sysv.shmmin 1
  set_sysctl kern.sysv.shmseg 48
  set_sysctl kern.sysv.shmall 131072000
  echo Settings applied.
  echo
  if $(launchctl list 2>/dev/null | grep -q '\.ReportCrash\>') ; then
    echo
    echo Unloading the default crash reporter
    SL=/System/Library; PL=com.apple.ReportCrash
    sudo -u "$SUDO_USER" launchctl unload -w ${SL}/LaunchAgents/${PL}.plist
    launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist
    echo
  fi
  echo It is recommended to disable System Integrity Protection for increased performance.
  echo See: https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection
  echo
  DONE=1
fi
if [ "$PLATFORM" = "Haiku" ] ; then
  DEBUG_SERVER_DIR=~/config/settings/system/debug_server
  [ ! -d ${DEBUG_SERVER_DIR} ] && mkdir -p ${DEBUG_SERVER_DIR}
  SETTINGS=${DEBUG_SERVER_DIR}/settings
  [ -r ${SETTINGS} ] && grep -qE "default_action\s+kill" ${SETTINGS} && { echo "Nothing to do"; } || { \
    echo We change the debug_server default_action from user to silently kill; \
    [ ! -r ${SETTINGS} ] && echo "default_action kill" >${SETTINGS} || { mv ${SETTINGS} s.tmp; sed -e "s/default_action\s\s*user/default_action kill/" s.tmp > ${SETTINGS}; rm s.tmp; }; \
    echo Settings applied.; echo; \
  }
  DONE=1
fi
test -z "$DONE" && echo Error: Unknown platform: $PLATFORM
exit 0
