blob: 0d9008fd187802fdf94091ad99416defd33e8fff (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
#!/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 [ "$#" -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

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"