[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20240819160309.2218114-1-vegard.nossum@oracle.com>
Date: Mon, 19 Aug 2024 18:02:57 +0200
From: Vegard Nossum <vegard.nossum@...cle.com>
To: Masahiro Yamada <masahiroy@...nel.org>, linux-kbuild@...r.kernel.org
Cc: Nathan Chancellor <nathan@...nel.org>, Nicolas Schier <nicolas@...sle.eu>,
Michael Ellerman <mpe@...erman.id.au>,
Morten Linderud <morten@...derud.pw>,
Haelwenn Monnier <contact@...odan.eu>, Jann Horn <jannh@...gle.com>,
Kees Cook <kees@...nel.org>,
James Bottomley <James.Bottomley@...senPartnership.com>,
Theodore Ts'o <tytso@....edu>, linux-hardening@...r.kernel.org,
Vegard Nossum <vegard.nossum@...cle.com>
Subject: [RFC PATCH 00/11] output a valid shell script when running 'make -n'
This patch series lets 'make -n' output a shell script that can be
used to build the kernel without any further use of make. For example:
make defconfig
# ensure some build prerequisites are built
make prepare
# generate build script
make -n | tee build.sh
# excecute build script
bash -eux build.sh
The purpose of this is to take a step towards defeating the insertion of
backdoors at build time (see [1]). Some of the benefits of separating the
build script from the build system are:
- we can invoke make in a restricted environment (e.g. mostly read-only
kernel tree),
- we have an audit log of the exact commands that run during the build
process; although it's true that the build script wouldn't be useful
for either production or development builds (as it doesn't support
incremental rebuilds or parallel builds), it would allow you to
rebuild an existing kernel and compare the resulting binary for
discrepancies to the original build,
- the audit log can be stored (e.g. in git) and changes to it over time
can themselves be audited (e.g. by looking at diffs),
- there's a lot fewer places to hide malicious code in a straight-line
shell script that makes minimal use of variables and helper functions.
You also cannot inject fragments of Makefile code through environment
variables (again, see [1]).
Alternative ways to achieve some of the same things would be:
- the existing compile_commands.json infrastructure; unfortunately this
does not include most of the steps performed during a build (such as
linking vmlinux) and does not really allow you to reproduce/verify the
full build,
- simply running something like strace -f -e trace=execve make; however,
this also does not result in something that can be easily played back;
at the very least it would need to be heavily filtered and processed
to account for data passed in environment variables and things like
temporary files used by the compiler.
This implementation works as follows:
- 'make -n' (AKA --dry-run) by default prints out the commands that make
runs; this output is modified to be usable as a shell script,
- we output 'make() { :; }' at the start of the script in order to make
all 'make' invocations in the resulting build script no-ops (GNU Make
will actually execute -- and print -- all recipe lines that include
$(MAKE), even when invoked with -n).
- we simplify the makefile rules in some cases to make the shell script
more readable; for example, we don't need the logic that extracts
dependencies from .c files (since that is only used by 'make' itself
when determining what to rebuild) or the logic that generates .cmd
files,
This patch is WIP and may not produce a working shell script in all
circumstances. For example, while plain 'make -n' works for me, other
make targets (e.g. 'make -n htmldocs') are not at all guaranteed to
produce meaningful output; certain kernel configs may also not work,
especially those that rely on external tools like e.g. Rust.
[1]: https://www.openwall.com/lists/oss-security/2024/04/17/3
[2]: https://www.gnu.org/software/make/manual/make.html#Testing-Flags
Vegard
---
Vegard Nossum (11):
kbuild: ignore .config rule for make --always-make
kbuild: document some prerequisites
kbuild: pass KERNELVERSION and LOCALVERSION explicitly to
setlocalversion
kbuild: don't execute .ko recipe in --dry-run mode
kbuild: execute modules.order recipe in --dry-run mode
kbuild: set $dry_run when running in --dry-run mode
kbuild: define 'make' as a no-op in --dry-run mode
kbuild: make link-vmlinux.sh respect $dry_run
kbuild: simplify commands in --dry-run mode
kbuild: don't test for file presence in --dry-run mode
kbuild: suppress echoing of commands in --dry-run mode
Makefile | 28 +++++++++++++++++---
arch/x86/boot/compressed/Makefile | 6 +++++
scripts/Kbuild.include | 27 +++++++++++++++++++
scripts/Makefile.build | 2 +-
scripts/Makefile.modfinal | 9 +++++--
scripts/Makefile.modpost | 8 ++++--
scripts/Makefile.vmlinux | 22 ++++++++++++++--
scripts/Makefile.vmlinux_o | 3 +++
scripts/link-vmlinux.sh | 44 ++++++++++++++++++++-----------
9 files changed, 123 insertions(+), 26 deletions(-)
--
2.34.1
Powered by blists - more mailing lists