1
0
mirror of https://github.com/systemd/systemd synced 2026-04-12 10:04:50 +02:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Jason A. Donenfeld
da2862ef06 random-seed: hash together old seed and new seed before writing out file
If we're consuming an on-disk seed, we usually write out a new one after
consuming it. In that case, we might be at early boot and the randomness
could be rather poor, and the kernel doesn't guarantee that it'll use
the new randomness right away for us. In order to prevent the new
entropy from getting any worse, hash together the old seed and the new
seed, and replace the final bytes of the new seed with the hash output.
This way, entropy strictly increases and never regresses.

Fixes: https://github.com/systemd/systemd/issues/21983
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2022-01-04 22:47:56 +00:00
Jan Janssen
948d085e89 boot: Add gdb support and documentation
This will finally allow debugging issues in systemd without resorting to
Print() calls all over the place.
2022-01-04 17:23:01 +01:00
7 changed files with 183 additions and 1 deletions

View File

@ -330,3 +330,43 @@ To debug systemd components other than PID 1, set "program" to the full path of
debug and set "processId" to "${command:pickProcess}". Now, when starting the debugger, VSCode will ask you debug and set "processId" to "${command:pickProcess}". Now, when starting the debugger, VSCode will ask you
the PID of the process you want to debug. Run `systemctl show --property MainPID --value <component>` in the the PID of the process you want to debug. Run `systemctl show --property MainPID --value <component>` in the
container to figure out the PID and enter it when asked and VSCode will attach to that process instead. container to figure out the PID and enter it when asked and VSCode will attach to that process instead.
# Debugging systemd-boot
During boot, systemd-boot and the stub loader will output a message like `systemd-boot@0x0A,0x0B`,
providing the location of the text and data sections. These location can then be used to attach
to a QEMU session (provided it was run with `-s`) with these gdb commands:
```
(gdb) file build/src/boot/efi/systemd-bootx64.efi
(gdb) add-symbol-file build/src/boot/efi/systemd_boot.so 0x0A -s .data 0x0B
(gdb) set architecture i386:x86-64
(gdb) target remote :1234
```
This process can be automated by using the `debug-sd-boot.sh` script in the tools folder. If run
without arguments it will provide usage information.
If the debugger is too slow to attach to examine an early boot code passage, we can uncomment the
call to `debug_break()` inside of `efi_main()`. As soon as the debugger has control we can then run
`set variable wait = 0` or `return` to continue. Once the debugger has attached, setting breakpoints
will work like usual.
To debug systemd-boot in an IDE such as VSCode we can use a launch configuration like this:
```json
{
"name": "systemd-boot",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/src/boot/efi/systemd-bootx64.efi",
"cwd": "${workspaceFolder}",
"MIMode": "gdb",
"miDebuggerServerAddress": ":1234",
"setupCommands": [
{ "text": "shell mkfifo /tmp/sdboot.{in,out}" },
{ "text": "shell qemu-system-x86_64 [...] -s -serial pipe:/tmp/sdboot" },
{ "text": "shell ${workspaceFolder}/tools/debug-sd-boot.sh ${workspaceFolder}/build/src/boot/efi/systemd-bootx64.efi /tmp/sdboot.out systemd-boot.gdb" },
{ "text": "source /tmp/systemd-boot.gdb" },
]
}
```

View File

@ -2353,6 +2353,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
InitializeLib(image, sys_table); InitializeLib(image, sys_table);
init_usec = time_usec(); init_usec = time_usec();
debug_hook(L"systemd-boot");
/* Uncomment the next line if you need to wait for debugger. */
// debug_break();
err = BS->OpenProtocol(image, err = BS->OpenProtocol(image,
&LoadedImageProtocol, &LoadedImageProtocol,

View File

@ -181,6 +181,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
EFI_STATUS err; EFI_STATUS err;
InitializeLib(image, sys_table); InitializeLib(image, sys_table);
debug_hook(L"systemd-stub");
/* Uncomment the next line if you need to wait for debugger. */
// debug_break();
err = BS->OpenProtocol( err = BS->OpenProtocol(
image, image,

View File

@ -738,3 +738,20 @@ UINT64 get_os_indications_supported(void) {
return osind; return osind;
} }
#ifdef EFI_DEBUG
__attribute__((noinline)) void debug_break(void) {
/* This is a poor programmer's breakpoint to wait until a debugger
* has attached to us. Just "set variable wait = 0" or "return" to continue. */
volatile BOOLEAN wait = TRUE;
while (wait)
/* Prefer asm based stalling so that gdb has a source location to present. */
#if defined(__i386__) || defined(__x86_64__)
asm volatile("pause");
#elif defined(__aarch64__)
asm volatile("wfi");
#else
BS->Stall(5000);
#endif
}
#endif

View File

@ -159,3 +159,13 @@ static inline void *PHYSICAL_ADDRESS_TO_POINTER(EFI_PHYSICAL_ADDRESS addr) {
} }
UINT64 get_os_indications_supported(void); UINT64 get_os_indications_supported(void);
#ifdef EFI_DEBUG
void debug_break(void);
extern UINT8 _text, _data;
/* Report the relocated position of text and data sections so that a debugger
* can attach to us. See debug-sd-boot.sh for how this can be done. */
# define debug_hook(identity) Print(identity L"@0x%x,0x%x\n", &_text, &_data)
#else
# define debug_hook(identity)
#endif

View File

@ -26,6 +26,7 @@
#include "random-util.h" #include "random-util.h"
#include "string-util.h" #include "string-util.h"
#include "sync-util.h" #include "sync-util.h"
#include "sha256.h"
#include "util.h" #include "util.h"
#include "xattr-util.h" #include "xattr-util.h"
@ -106,9 +107,11 @@ static int run(int argc, char *argv[]) {
_cleanup_close_ int seed_fd = -1, random_fd = -1; _cleanup_close_ int seed_fd = -1, random_fd = -1;
bool read_seed_file, write_seed_file, synchronous; bool read_seed_file, write_seed_file, synchronous;
_cleanup_free_ void* buf = NULL; _cleanup_free_ void* buf = NULL;
struct sha256_ctx hash_state;
uint8_t hash[32];
size_t buf_size; size_t buf_size;
struct stat st; struct stat st;
ssize_t k; ssize_t k, l;
int r; int r;
log_setup(); log_setup();
@ -242,6 +245,16 @@ static int run(int argc, char *argv[]) {
if (r < 0) if (r < 0)
log_error_errno(r, "Failed to write seed to /dev/urandom: %m"); log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
} }
/* If we're going to later write out a seed file, initialize a hash state with
* the contents of the seed file we just read, so that the new one can't regress
* in entropy. */
if (write_seed_file) {
sha256_init_ctx(&hash_state);
if (k < 0)
k = 0;
sha256_process_bytes(&k, sizeof(k), &hash_state);
sha256_process_bytes(buf, k, &hash_state);
}
} }
if (write_seed_file) { if (write_seed_file) {
@ -277,6 +290,17 @@ static int run(int argc, char *argv[]) {
"Got EOF while reading from /dev/urandom."); "Got EOF while reading from /dev/urandom.");
} }
/* If we previously read in a seed file, then hash the new seed into the old one,
* and replace the last 32 bytes of the seed with the hash output, so that the
* new seed file can't regress in entropy. */
if (read_seed_file) {
sha256_process_bytes(&k, sizeof(k), &hash_state);
sha256_process_bytes(buf, k, &hash_state);
sha256_finish_ctx(&hash_state, hash);
l = MIN(k, 32);
memcpy((uint8_t *)buf + k - l, hash, l);
}
r = loop_write(seed_fd, buf, (size_t) k, false); r = loop_write(seed_fd, buf, (size_t) k, false);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to write new random seed file: %m"); return log_error_errno(r, "Failed to write new random seed file: %m");

85
tools/debug-sd-boot.sh Executable file
View File

@ -0,0 +1,85 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
if [[ $# -lt 2 ]]; then
echo "Usage: ${0} TARGET INPUT [GDBSCRIPT]"
echo "Debug systemd-boot/stub in QEMU."
echo
echo "TARGET should point to the EFI binary to be examined inside the"
echo "build directory (systemd-boot\$ARCH.efi or linux\$arch.efi.stub)."
echo
echo "INPUT should point to the QEMU serial output pipe. This is used to"
echo "extract the location of the symbols. For this to work, QEMU must"
echo "be run with '-s -serial pipe:PATH'. Note that QEMU will append"
echo ".in/.out to the path, while this script expects the out pipe directly."
echo
echo "If GDBSCRIPT is empty, gdb is run directly attached to the boot"
echo "loader, otherwise a script is generated in the given path that allows"
echo "attaching manually like this:"
echo " (gdb) source GDBSCRIPT"
echo " (gdb) target remote :1234"
echo
echo "Exmaple usage:"
echo " mkfifo /tmp/sdboot.{in,out}"
echo " qemu-system-x86_64 [...] -s -serial pipe:/tmp/sdboot"
echo " ./tools/debug-sd-boot.sh ./build/src/boot/efi/systemd-bootx64.efi \\"
echo " /tmp/sdboot.out"
exit 1
fi
binary=$(realpath "${1}")
if [[ "${1}" =~ systemd-boot([[:alnum:]]+).efi ]]; then
target="systemd-boot"
symbols=$(realpath "$(dirname "${1}")/systemd_boot.so")
elif [[ "${1}" =~ linux([[:alnum:]]+).efi.stub ]]; then
target="systemd-stub"
symbols=$(realpath "$(dirname "${1}")/linux${BASH_REMATCH[1]}.elf.stub")
else
echo "Cannot detect EFI binary '${1}'."
exit 1
fi
case "${BASH_REMATCH[1]}" in
ia32) arch="i386";;
x64) arch="i386:x86-64";;
aa64) arch="aarch64";;
arm|riscv64) arch="${BASH_REMATCH[1]}";;
*)
echo "Unknown EFI arch '${BASH_REMATCH[1]}'."
exit 1
esac
# system-boot will print out a line like this to inform us where gdb is supposed to
# look for .text and .data section:
# systemd-boot@0x0,0x0
while read -r line; do
if [[ "${line}" =~ ${target}@(0x[[:xdigit:]]+),(0x[[:xdigit:]]+) ]]; then
text="${BASH_REMATCH[1]}"
data="${BASH_REMATCH[2]}"
break
fi
done < "${2}"
if [[ -z "${text}" || -z "${data}" ]]; then
echo "Could not determine text and data location."
exit 1
fi
if [[ -z "${3}" ]]; then
gdb_script=$(mktemp /tmp/debug-sd-boot.XXXXXX.gdb)
trap 'rm -f "${gdb_script}"' EXIT
else
gdb_script="${3}"
fi
echo "file ${binary}
add-symbol-file ${symbols} ${text} -s .data ${data}
set architecture ${arch}" > "${gdb_script}"
if [[ -z "${3}" ]]; then
gdb -x "${gdb_script}" -ex "target remote :1234"
else
echo "GDB script written to '${gdb_script}'."
fi