10 # writeCBin from trivial-builders won't let us choose
12 writeCBinWithStdenv = codePath: stdenv': env: runCommandWith {
17 preferLocalBuild = true;
18 allowSubstitutes = false;
21 [ -n "$preBuild" ] && eval "$preBuild"
23 mkdir -p "$(dirname "$n")"
25 NIX_DEBUG=1 $CC -x c code.c -O1 $TEST_EXTRA_FLAGS -o "$n"
28 f1exampleWithStdEnv = writeCBinWithStdenv ./fortify1-example.c;
29 f2exampleWithStdEnv = writeCBinWithStdenv ./fortify2-example.c;
30 f3exampleWithStdEnv = writeCBinWithStdenv ./fortify3-example.c;
32 stdenvUnsupport = additionalUnsupported: stdenv.override {
33 cc = stdenv.cc.override {
34 cc = (lib.extendDerivation true {
35 hardeningUnsupportedFlags = (stdenv.cc.cc.hardeningUnsupportedFlags or []) ++ additionalUnsupported;
38 allowedRequisites = null;
41 checkTestBin = testBin: {
42 # can only test flags that are detectable by hardening-check
47 ignoreStackProtector ? true,
48 expectFailure ? false,
50 expectFailureClause = lib.optionalString expectFailure
51 " && echo 'ERROR: Expected hardening-check to fail, but it passed!' >&2 && exit 1";
52 in runCommandCC "check-test-bin" {
53 nativeBuildInputs = [ debian-devscripts ];
54 buildInputs = [ testBin ];
55 meta.platforms = lib.platforms.linux; # ELF-reliant
57 hardening-check --nocfprotection \
58 ${lib.optionalString ignoreBindNow "--nobindnow"} \
59 ${lib.optionalString ignoreFortify "--nofortify"} \
60 ${lib.optionalString ignorePie "--nopie"} \
61 ${lib.optionalString ignoreRelRO "--norelro"} \
62 ${lib.optionalString ignoreStackProtector "--nostackprotector"} \
63 $(PATH=$HOST_PATH type -P test-bin) ${expectFailureClause}
67 nameDrvAfterAttrName = builtins.mapAttrs (name: drv:
68 drv.overrideAttrs (_: { name = "test-${name}"; })
71 # returning a specific exit code when aborting due to a fortify
72 # check isn't mandated. so it's better to just ensure that a
73 # nonzero exit code is returned when we go a single byte beyond
74 # the buffer, with the example programs being designed to be
75 # unlikely to genuinely segfault for such a small overflow.
76 fortifyExecTest = testBin: runCommand "exec-test" {
80 meta.broken = !(stdenv.buildPlatform.canExecute stdenv.hostPlatform);
83 export PATH=$HOST_PATH
84 echo "Saturated buffer:" # check program isn't completly broken
86 echo "One byte too far:" # eighth byte being the null terminator
87 (! test-bin 0123456 7) || (echo 'Expected failure, but succeeded!' && exit 1)
89 echo "Expected behaviour observed"
93 brokenIf = cond: drv: if cond then drv.overrideAttrs (old: { meta = old.meta or {} // { broken = true; }; }) else drv;
95 in nameDrvAfterAttrName ({
96 bindNowExplicitEnabled = brokenIf stdenv.hostPlatform.isStatic (checkTestBin (f2exampleWithStdEnv stdenv {
97 hardeningEnable = [ "bindnow" ];
99 ignoreBindNow = false;
102 # musl implementation undetectable by this means even if present
103 fortifyExplicitEnabled = brokenIf stdenv.hostPlatform.isMusl (checkTestBin (f2exampleWithStdEnv stdenv {
104 hardeningEnable = [ "fortify" ];
106 ignoreFortify = false;
109 fortify1ExplicitEnabledExecTest = fortifyExecTest (f1exampleWithStdEnv stdenv {
110 hardeningEnable = [ "fortify" ];
113 # musl implementation is effectively FORTIFY_SOURCE=1-only,
114 # clang-on-glibc also only appears to support FORTIFY_SOURCE=1 (!)
115 fortifyExplicitEnabledExecTest = brokenIf (
116 stdenv.hostPlatform.isMusl || (stdenv.cc.isClang && stdenv.hostPlatform.libc == "glibc")
117 ) (fortifyExecTest (f2exampleWithStdEnv stdenv {
118 hardeningEnable = [ "fortify" ];
121 fortify3ExplicitEnabled = brokenIf (
122 stdenv.hostPlatform.isMusl || !stdenv.cc.isGNU || lib.versionOlder stdenv.cc.version "12"
123 ) (checkTestBin (f3exampleWithStdEnv stdenv {
124 hardeningEnable = [ "fortify3" ];
126 ignoreFortify = false;
129 # musl implementation is effectively FORTIFY_SOURCE=1-only
130 fortify3ExplicitEnabledExecTest = brokenIf (
131 stdenv.hostPlatform.isMusl || !stdenv.cc.isGNU || lib.versionOlder stdenv.cc.version "12"
132 ) (fortifyExecTest (f3exampleWithStdEnv stdenv {
133 hardeningEnable = [ "fortify3" ];
136 pieExplicitEnabled = brokenIf stdenv.hostPlatform.isStatic (checkTestBin (f2exampleWithStdEnv stdenv {
137 hardeningEnable = [ "pie" ];
142 relROExplicitEnabled = checkTestBin (f2exampleWithStdEnv stdenv {
143 hardeningEnable = [ "relro" ];
148 stackProtectorExplicitEnabled = brokenIf stdenv.hostPlatform.isStatic (checkTestBin (f2exampleWithStdEnv stdenv {
149 hardeningEnable = [ "stackprotector" ];
151 ignoreStackProtector = false;
154 bindNowExplicitDisabled = checkTestBin (f2exampleWithStdEnv stdenv {
155 hardeningDisable = [ "bindnow" ];
157 ignoreBindNow = false;
158 expectFailure = true;
161 fortifyExplicitDisabled = checkTestBin (f2exampleWithStdEnv stdenv {
162 hardeningDisable = [ "fortify" ];
164 ignoreFortify = false;
165 expectFailure = true;
168 fortify3ExplicitDisabled = checkTestBin (f3exampleWithStdEnv stdenv {
169 hardeningDisable = [ "fortify3" ];
171 ignoreFortify = false;
172 expectFailure = true;
175 fortifyExplicitDisabledDisablesFortify3 = checkTestBin (f3exampleWithStdEnv stdenv {
176 hardeningEnable = [ "fortify3" ];
177 hardeningDisable = [ "fortify" ];
179 ignoreFortify = false;
180 expectFailure = true;
183 fortify3ExplicitDisabledDoesntDisableFortify = checkTestBin (f2exampleWithStdEnv stdenv {
184 hardeningEnable = [ "fortify" ];
185 hardeningDisable = [ "fortify3" ];
187 ignoreFortify = false;
190 pieExplicitDisabled = brokenIf (
191 stdenv.hostPlatform.isMusl && stdenv.cc.isClang
192 ) (checkTestBin (f2exampleWithStdEnv stdenv {
193 hardeningDisable = [ "pie" ];
196 expectFailure = true;
199 # can't force-disable ("partial"?) relro
200 relROExplicitDisabled = brokenIf true (checkTestBin (f2exampleWithStdEnv stdenv {
201 hardeningDisable = [ "pie" ];
204 expectFailure = true;
207 stackProtectorExplicitDisabled = checkTestBin (f2exampleWithStdEnv stdenv {
208 hardeningDisable = [ "stackprotector" ];
210 ignoreStackProtector = false;
211 expectFailure = true;
214 # most flags can't be "unsupported" by compiler alone and
215 # binutils doesn't have an accessible hardeningUnsupportedFlags
216 # mechanism, so can only test a couple of flags through altered
219 fortifyStdenvUnsupp = checkTestBin (f2exampleWithStdEnv (stdenvUnsupport ["fortify"]) {
220 hardeningEnable = [ "fortify" ];
222 ignoreFortify = false;
223 expectFailure = true;
226 fortify3StdenvUnsupp = checkTestBin (f3exampleWithStdEnv (stdenvUnsupport ["fortify3"]) {
227 hardeningEnable = [ "fortify3" ];
229 ignoreFortify = false;
230 expectFailure = true;
233 fortifyStdenvUnsuppUnsupportsFortify3 = checkTestBin (f3exampleWithStdEnv (stdenvUnsupport ["fortify"]) {
234 hardeningEnable = [ "fortify3" ];
236 ignoreFortify = false;
237 expectFailure = true;
240 fortify3StdenvUnsuppDoesntUnsuppFortify = brokenIf stdenv.hostPlatform.isMusl (checkTestBin (f2exampleWithStdEnv (stdenvUnsupport ["fortify3"]) {
241 hardeningEnable = [ "fortify" ];
243 ignoreFortify = false;
246 fortify3StdenvUnsuppDoesntUnsuppFortifyExecTest = fortifyExecTest (f2exampleWithStdEnv (stdenvUnsupport ["fortify3"]) {
247 hardeningEnable = [ "fortify" ];
250 stackProtectorStdenvUnsupp = checkTestBin (f2exampleWithStdEnv (stdenvUnsupport ["stackprotector"]) {
251 hardeningEnable = [ "stackprotector" ];
253 ignoreStackProtector = false;
254 expectFailure = true;
257 # NIX_HARDENING_ENABLE set in the shell overrides hardeningDisable
258 # and hardeningEnable
260 stackProtectorReenabledEnv = checkTestBin (f2exampleWithStdEnv stdenv {
261 hardeningDisable = [ "stackprotector" ];
263 export NIX_HARDENING_ENABLE="stackprotector"
266 ignoreStackProtector = false;
269 stackProtectorReenabledFromAllEnv = checkTestBin (f2exampleWithStdEnv stdenv {
270 hardeningDisable = [ "all" ];
272 export NIX_HARDENING_ENABLE="stackprotector"
275 ignoreStackProtector = false;
278 stackProtectorRedisabledEnv = checkTestBin (f2exampleWithStdEnv stdenv {
279 hardeningEnable = [ "stackprotector" ];
281 export NIX_HARDENING_ENABLE=""
284 ignoreStackProtector = false;
285 expectFailure = true;
288 fortify3EnabledEnvEnablesFortify = brokenIf stdenv.hostPlatform.isMusl (checkTestBin (f2exampleWithStdEnv stdenv {
289 hardeningDisable = [ "fortify" "fortify3" ];
291 export NIX_HARDENING_ENABLE="fortify3"
294 ignoreFortify = false;
297 fortify3EnabledEnvEnablesFortifyExecTest = fortifyExecTest (f2exampleWithStdEnv stdenv {
298 hardeningDisable = [ "fortify" "fortify3" ];
300 export NIX_HARDENING_ENABLE="fortify3"
304 fortifyEnabledEnvDoesntEnableFortify3 = checkTestBin (f3exampleWithStdEnv stdenv {
305 hardeningDisable = [ "fortify" "fortify3" ];
307 export NIX_HARDENING_ENABLE="fortify"
310 ignoreFortify = false;
311 expectFailure = true;
314 # NIX_HARDENING_ENABLE can't enable an unsupported feature
316 stackProtectorUnsupportedEnabledEnv = checkTestBin (f2exampleWithStdEnv (stdenvUnsupport ["stackprotector"]) {
318 export NIX_HARDENING_ENABLE="stackprotector"
321 ignoreStackProtector = false;
322 expectFailure = true;
325 # undetectable by this means on static even if present
326 fortify1ExplicitEnabledCmdlineDisabled = brokenIf stdenv.hostPlatform.isStatic (checkTestBin (f1exampleWithStdEnv stdenv {
327 hardeningEnable = [ "fortify" ];
329 export TEST_EXTRA_FLAGS='-D_FORTIFY_SOURCE=0'
332 ignoreFortify = false;
333 expectFailure = true;
336 # musl implementation undetectable by this means even if present
337 fortify1ExplicitDisabledCmdlineEnabled = brokenIf (
338 stdenv.hostPlatform.isMusl || stdenv.hostPlatform.isStatic
339 ) (checkTestBin (f1exampleWithStdEnv stdenv {
340 hardeningDisable = [ "fortify" ];
342 export TEST_EXTRA_FLAGS='-D_FORTIFY_SOURCE=1'
345 ignoreFortify = false;
348 fortify1ExplicitDisabledCmdlineEnabledExecTest = fortifyExecTest (f1exampleWithStdEnv stdenv {
349 hardeningDisable = [ "fortify" ];
351 export TEST_EXTRA_FLAGS='-D_FORTIFY_SOURCE=1'
355 fortify1ExplicitEnabledCmdlineDisabledNoWarn = f1exampleWithStdEnv stdenv {
356 hardeningEnable = [ "fortify" ];
358 export TEST_EXTRA_FLAGS='-D_FORTIFY_SOURCE=0 -Werror'
363 tb = f2exampleWithStdEnv stdenv {
364 hardeningDisable = [ "all" ];
365 hardeningEnable = [ "fortify" "pie" ];
369 allExplicitDisabledBindNow = checkTestBin tb {
370 ignoreBindNow = false;
371 expectFailure = true;
374 allExplicitDisabledFortify = checkTestBin tb {
375 ignoreFortify = false;
376 expectFailure = true;
379 allExplicitDisabledPie = brokenIf (
380 stdenv.hostPlatform.isMusl && stdenv.cc.isClang
383 expectFailure = true;
386 # can't force-disable ("partial"?) relro
387 allExplicitDisabledRelRO = brokenIf true (checkTestBin tb {
389 expectFailure = true;
392 allExplicitDisabledStackProtector = checkTestBin tb {
393 ignoreStackProtector = false;
394 expectFailure = true;