about summary refs log tree commit diff
path: root/pkgs/build-support/cc-wrapper/add-hardening.sh
blob: 0dca3b3347e5b200193c843f701fad0eef7263af (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
declare -a hardeningCFlagsAfter=()
declare -a hardeningCFlagsBefore=()

declare -A hardeningEnableMap=()

# Intentionally word-split in case 'NIX_HARDENING_ENABLE' is defined in Nix. The
# array expansion also prevents undefined variables from causing trouble with
# `set -u`.
for flag in ${NIX_HARDENING_ENABLE_@suffixSalt@-}; do
  hardeningEnableMap["$flag"]=1
done

# fortify3 implies fortify enablement - make explicit before
# we filter unsupported flags because unsupporting fortify3
# doesn't mean we should unsupport fortify too
if [[ -n "${hardeningEnableMap[fortify3]-}" ]]; then
  hardeningEnableMap["fortify"]=1
fi

# Remove unsupported flags.
for flag in @hardening_unsupported_flags@; do
  unset -v "hardeningEnableMap[$flag]"
  # fortify being unsupported implies fortify3 is unsupported
  if [[ "$flag" = 'fortify' ]] ; then
    unset -v "hardeningEnableMap['fortify3']"
  fi
done

# now make fortify and fortify3 mutually exclusive
if [[ -n "${hardeningEnableMap[fortify3]-}" ]]; then
  unset -v "hardeningEnableMap['fortify']"
fi

if (( "${NIX_DEBUG:-0}" >= 1 )); then
  declare -a allHardeningFlags=(fortify fortify3 stackprotector stackclashprotection pie pic strictoverflow format trivialautovarinit zerocallusedregs)
  declare -A hardeningDisableMap=()

  # Determine which flags were effectively disabled so we can report below.
  for flag in "${allHardeningFlags[@]}"; do
    if [[ -z "${hardeningEnableMap[$flag]-}" ]]; then
      hardeningDisableMap["$flag"]=1
    fi
  done

  printf 'HARDENING: disabled flags:' >&2
  (( "${#hardeningDisableMap[@]}" )) && printf ' %q' "${!hardeningDisableMap[@]}" >&2
  echo >&2

  if (( "${#hardeningEnableMap[@]}" )); then
    echo 'HARDENING: Is active (not completely disabled with "all" flag)' >&2;
  fi
fi

for flag in "${!hardeningEnableMap[@]}"; do
  case $flag in
    fortify | fortify3)
      # Use -U_FORTIFY_SOURCE to avoid warnings on toolchains that explicitly
      # set -D_FORTIFY_SOURCE=0 (like 'clang -fsanitize=address').
      hardeningCFlagsBefore+=('-O2' '-U_FORTIFY_SOURCE')
      # Unset any _FORTIFY_SOURCE values the command-line may have set before
      # enforcing our own value, avoiding (potentially fatal) redefinition
      # warnings
      hardeningCFlagsAfter+=('-U_FORTIFY_SOURCE')
      case $flag in
        fortify)
          if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling fortify >&2; fi
          hardeningCFlagsAfter+=('-D_FORTIFY_SOURCE=2')
        ;;
        fortify3)
          if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling fortify3 >&2; fi
          hardeningCFlagsAfter+=('-D_FORTIFY_SOURCE=3')
        ;;
        *)
          # Ignore unsupported.
          ;;
      esac
      ;;
    stackprotector)
      if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling stackprotector >&2; fi
      hardeningCFlagsBefore+=('-fstack-protector-strong' '--param' 'ssp-buffer-size=4')
      ;;
    stackclashprotection)
      if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling stack-clash-protection >&2; fi
      hardeningCFlagsBefore+=('-fstack-clash-protection')
      ;;
    pie)
      # NB: we do not use `+=` here, because PIE flags must occur before any PIC flags
      if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling CFlags -fPIE >&2; fi
      hardeningCFlagsBefore=('-fPIE' "${hardeningCFlagsBefore[@]}")
      if [[ ! (" ${params[*]} " =~ " -shared " || " ${params[*]} " =~ " -static ") ]]; then
        if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling LDFlags -pie >&2; fi
        hardeningCFlagsBefore=('-pie' "${hardeningCFlagsBefore[@]}")
      fi
      ;;
    pic)
      if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling pic >&2; fi
      hardeningCFlagsBefore+=('-fPIC')
      ;;
    strictoverflow)
      if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling strictoverflow >&2; fi
      if (( @isClang@ )); then
        # In Clang, -fno-strict-overflow only serves to set -fwrapv and is
        # reported as an unused CLI argument if -fwrapv or -fno-wrapv is set
        # explicitly, so we side step that by doing the conversion here.
        #
        # See: https://github.com/llvm/llvm-project/blob/llvmorg-16.0.6/clang/lib/Driver/ToolChains/Clang.cpp#L6315
        #
        hardeningCFlagsBefore+=('-fwrapv')
      else
        hardeningCFlagsBefore+=('-fno-strict-overflow')
      fi
      ;;
    trivialautovarinit)
      if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling trivialautovarinit >&2; fi
      hardeningCFlagsBefore+=('-ftrivial-auto-var-init=pattern')
      ;;
    format)
      if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling format >&2; fi
      hardeningCFlagsBefore+=('-Wformat' '-Wformat-security' '-Werror=format-security')
      ;;
    zerocallusedregs)
      if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling zerocallusedregs >&2; fi
      hardeningCFlagsBefore+=('-fzero-call-used-regs=used-gpr')
      ;;
    *)
      # Ignore unsupported. Checked in Nix that at least *some*
      # tool supports each flag.
      ;;
  esac
done