blob: a6d10dc6902ef9f5dba7b45d6ed22d3a5a67277e (plain)
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
#!/bin/sh

# Build cgit for a release with Linux hardening flags, which are ELF and GCC or
# Clang specific. The flags add a stack protector, fortified libc calls, a
# position independent executable, and full RELRO. By default Lua is pinned off
# so the binary needs no Lua at runtime, and running it as
# ./tools/release-build.sh lua links in the backend behind the "lua:" filter
# prefix instead, which needs a Lua dev package installed.

set -eu

if test "$#" -gt 1
then
	echo "usage: $0 [lua]" >&2
	exit 2
fi
case "${1:-}" in
"")
	set -- NO_LUA=1
	;;
lua)
	set --
	;;
*)
	echo "$0: unknown argument \"$1\", expected \"lua\" or nothing" >&2
	exit 2
	;;
esac

# Every make target below is written relative to the repository root.
cd "$(dirname "$0")/.."

CC=${CC:-cc}

# Not every hardening flag exists on every toolchain, and an unknown one would
# fail the build rather than be ignored, so the ones that might be missing are
# compiled before they are used. The argument is left unquoted so a caller can
# probe several flags at once.
supports() {
	printf 'int main(void){return 0;}\n' >"$probe.c"
	$CC $1 -o "$probe.out" "$probe.c" >/dev/null 2>&1
}

# The template is spelled out rather than passed with -t, whose handling of a
# prefix differs between the GNU and BSD versions.
probe=$(mktemp "${TMPDIR:-/tmp}/cgit-probe.XXXXXX")
trap 'rm -f "$probe" "$probe.c" "$probe.out"' EXIT

CFLAGS="-O2 -g -Wall -fstack-protector-strong -fPIE -fno-plt"

# Level 3 adds the bounds checks level 2 could not prove, and needs GCC 12 or
# Clang 15. Fall back rather than lose fortification altogether.
if supports "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -O2"; then
	CFLAGS="$CFLAGS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3"
else
	CFLAGS="$CFLAGS -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2"
fi

# Without this a large stack frame can step over a guard page rather than
# touch it.
if supports "-fstack-clash-protection"; then
	CFLAGS="$CFLAGS -fstack-clash-protection"
fi

# The debug info otherwise records the absolute build directory, so the same
# sources built in two checkouts would differ.
if supports "-ffile-prefix-map=/x=."; then
	CFLAGS="$CFLAGS -ffile-prefix-map=$PWD=."
fi

LDFLAGS="-pie -Wl,-z,relro,-z,now -Wl,-z,noexecstack"

# These reach only the cgit objects, so git's own sources are not held to them.
# -Wformat-security is an error because a non-literal format with no arguments
# is never intentional. -Wformat-nonliteral would catch the module-link shape,
# a non-literal that does take arguments, but it also fires on va_list
# forwarding and on local format constants, so it is left to manual runs when
# touching anything that formats.
#
#     make cgit CGIT_EXTRA_CFLAGS=-Wformat-nonliteral
#
CGIT_EXTRA_CFLAGS="-Wformat -Wformat-security -Werror=format-security"

# The build starts from clean so the bundled git objects pick up the same
# flags, and cleanall rather than clean because only cleanall descends into
# vendor/git.
make cleanall
exec make "$@" CFLAGS="$CFLAGS" LDFLAGS="$LDFLAGS" \
	CGIT_EXTRA_CFLAGS="$CGIT_EXTRA_CFLAGS"