1
0
mirror of https://github.com/systemd/systemd synced 2026-03-25 00:04:53 +01:00

Compare commits

...

23 Commits

Author SHA1 Message Date
Yu Watanabe
8908ceb7a8
Merge pull request #20432 from yuwata/network-recreate-stacked-netdevs
network: recreate stacked netdevs when underlying device is re-added
2021-08-13 17:31:28 +09:00
Jan Janssen
702d40270a meson: Make unused-function an error 2021-08-13 09:23:45 +02:00
Steven Siloti
8a33aa199d resolved: retry on SERVFAIL before downgrading feature level
The SERVFAIL RCODE can be generated for many reasons which may not be related
to lack of feature support. For example, the Stubby resolver generates
SERVFAIL when a request times out. Such transient failures can cause
unnecessary downgrades to both the transaction and the server's feature level.
The consequences of this are especially severe if the server is in DNSSEC
strict mode. In this case repeated downgrades eventually cause the server to
stop resolving entirely with the error "incompatible-server".

To avoid unnecessary downgrades the request should be retried once with the
current level before the transaction's feature level is downgraded.
2021-08-13 09:23:12 +02:00
Lennart Poettering
d8151fb949
Merge pull request #20233 from maanyagoenka/log-error
systemd-analyze: add option to return an error value when unit verification fails
2021-08-13 09:22:48 +02:00
Lennart Poettering
2c3735d6ba
Merge pull request #20350 from medhefgo/boot
Grab bag of sd-boot improvements
2021-08-13 09:22:12 +02:00
Maanya Goenka
3cc3dc7736 systemd-analyze: option to exit with an error when 'verify' fails
The commit introduces a callback invoked from log_syntax_internal.
Use it from systemd-analyze to gather a list of units that contain
syntax warnings. A new command line option is added to make use of this.

The new option --recursive-errors takes in three possible modes:

1. yes - which is the default. systemd-analyze exits with an error when syntax warnings arise during verification of the
	 specified units or any of their dependencies.
3. no - systemd-analyze exits with an error when syntax warnings arise during verification of only the selected unit.
	Analyzing and loading any dependencies will be skipped.
4. one - systemd-analyze exits with an error when syntax warnings arise during verification
	 of only the selected units and their direct dependencies.

Below are two service unit files that I created for the purposes of testing:

1. First, we run the commands on a unit that does not have dependencies but has a non-existing key-value setting (i.e. foo = bar).

> cat <<EOF>testcase.service

[Unit]
foo = bar

[Service]
ExecStart = echo hello
EOF

OUTPUT:

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify testcase.service
/home/maanya-goenka/systemd/testcase.service:2: Unknown key name 'foo' in section 'Unit', ignoring.
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=yes testcase.service
/home/maanya-goenka/systemd/testcase.service:2: Unknown key name 'foo' in section 'Unit', ignoring.
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=no testcase.service
/home/maanya-goenka/systemd/testcase.service:2: Unknown key name 'foo' in section 'Unit', ignoring.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=one testcase.service
/home/maanya-goenka/systemd/testcase.service:2: Unknown key name 'foo' in section 'Unit', ignoring.
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

2. Next, we run the commands on a unit that is syntactically valid but has a non-existing dependency (i.e. foo2.service)

> cat <<EOF>foobar.service

[Unit]
Requires = foo2.service

[Service]
ExecStart = echo hello
EOF

OUTPUT:

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify foobar.service
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
foobar.service: Failed to create foobar.service/start: Unit foo2.service not found.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=yes foobar.service
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
foobar.service: Failed to create foobar.service/start: Unit foo2.service not found.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=no foobar.service
maanya-goenka@debian:~/systemd (log-error)$ echo $?
0

maanya-goenka@debian:~/systemd (log-error)$ sudo build/systemd-analyze verify --recursive-errors=one foobar.service
/usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed.
/usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly.
/usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether.
foobar.service: Failed to create foobar.service/start: Unit foo2.service not found.
maanya-goenka@debian:~/systemd (log-error)$ echo $?
1
2021-08-12 07:22:15 -07:00
Maanya Goenka
f14d6810e0 manager: add a test flag to ignore dependencies
The MANAGER_TEST_RUN_IGNORE_DEPENDENCIES flag was added in order to allow the caller
to skip the recursive loading of dependency units when loading specific
unit files. This includes the default dependencies, the specified dependencies, the slice.
This will be used by systemd-analyze to allow checking individual unit files in isolation.
2021-08-12 07:22:15 -07:00
Maanya Goenka
1545051c79 manager: use FLAGS_SET when checking for MANAGER_TEST_RUN_MINIMAL
Allows multiple flags to be set, for example, in systemd-analyze.
2021-08-12 07:22:15 -07:00
Jan Janssen
dba0c9832b sd-boot: Allow on/off and t/f for booleans too 2021-08-12 16:10:06 +02:00
Jan Janssen
ec97e40c29 sd-boot: Provide error messages when parsing a config option fails 2021-08-12 16:10:06 +02:00
Jan Janssen
e98d271e57 sd-boot: Rework console input handling
Fixes: #15847
Probably fixes: #19191
2021-08-12 16:10:02 +02:00
Jan Janssen
b3fc3a3ced sd-boot: Use StrSize where it makes sense 2021-08-12 09:48:37 +02:00
Jan Janssen
508df915b6 sd-boot: Assert all the things! 2021-08-12 09:48:37 +02:00
Jan Janssen
7a7267bf55 sd-boot: Add assert implementation
There is a ASSERT() macro from gnu-efi, but that does not show any
output to ConOut. Having to do some additional setup just to get
some debug output is tedious and outright difficult on real hardware.
2021-08-12 09:48:37 +02:00
Jan Janssen
b52fafb26d sd-boot: Fix possible null pointer dereference
Auto entries are showing garbage for the version in print_status()
because StrDuplicate does not expect null pointers.
2021-08-12 09:48:37 +02:00
Jan Janssen
b2cf9922a4 sd-boot: Don't use magic integer constants 2021-08-12 09:48:37 +02:00
Jan Janssen
8aba0eec49 sd-boot: Unify error handling
log_error_stall() and log_error_status_stall() will ensure the user has
a chance to catch an error message by stalling and also forcing a
lightred/black color on it. Also, convert several Print() calls to it
since they are actually error messages.
2021-08-12 09:48:27 +02:00
Yu Watanabe
1d0c9bd753 test-network: add a testcase for recreating stacked netdevs 2021-08-12 15:03:50 +09:00
Yu Watanabe
2f117922d4 network: recreate stacked netdevs when underlying device is re-added
Closes #20430.
2021-08-12 15:03:47 +09:00
Yu Watanabe
b14686ff3a network: adjust log messages, function names, etc. 2021-08-12 14:39:39 +09:00
Yu Watanabe
8f65304c5d network: use netdev_enter_failed() instead of netdev_drop() on error
Preparation for later commits to support reconfiguring netdevs.
2021-08-12 14:23:57 +09:00
Yu Watanabe
798e5dc8ae network: introduce a helper function netdev_is_stacked_and_independent() 2021-08-12 14:10:45 +09:00
Jan Janssen
f862e84724 macro: Move some macros to macro-fundamental.h
Also, make sure STRLEN works with wide strings too.
2021-08-11 14:59:46 +02:00
46 changed files with 876 additions and 424 deletions

View File

@ -41,8 +41,8 @@
a comment line. Empty and comment lines are ignored.</para> a comment line. Empty and comment lines are ignored.</para>
<para>Boolean arguments may be written as <para>Boolean arguments may be written as
<literal>yes</literal>/<literal>y</literal>/<literal>true</literal>/<literal>1</literal> or <literal>yes</literal>/<literal>y</literal>/<literal>true</literal>/<literal>t</literal>/<literal>on</literal>/<literal>1</literal> or
<literal>no</literal>/<literal>n</literal>/<literal>false</literal>/<literal>0</literal>. <literal>no</literal>/<literal>n</literal>/<literal>false</literal>/<literal>f</literal>/<literal>off</literal>/<literal>0</literal>.
</para> </para>
</refsect1> </refsect1>

View File

@ -744,6 +744,18 @@ Service b@0.service not loaded, b.socket cannot be started.
generators enabled will generally result in some warnings.</para></listitem> generators enabled will generally result in some warnings.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--recursive-errors=<replaceable>MODE</replaceable></option></term>
<listitem><para>Control verification of units and their dependencies and whether
<command>systemd-analyze verify</command> exits with a non-zero process exit status or not. With
<command>yes</command>, return a non-zero process exit status when warnings arise during verification
of either the specified unit or any of its associated dependencies. This is the default. With
<command>no</command>, return a non-zero process exit status when warnings arise during verification
of only the specified unit. With <command>one</command>, return a non-zero process exit status when
warnings arise during verification of either the specified unit or its immediate dependencies. </para></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--root=<replaceable>PATH</replaceable></option></term> <term><option>--root=<replaceable>PATH</replaceable></option></term>

View File

@ -345,6 +345,7 @@ possible_common_cc_flags = [
'-Werror=shift-count-overflow', '-Werror=shift-count-overflow',
'-Werror=shift-overflow=2', '-Werror=shift-overflow=2',
'-Werror=undef', '-Werror=undef',
'-Werror=unused-function',
'-Wfloat-equal', '-Wfloat-equal',
'-Wimplicit-fallthrough=5', '-Wimplicit-fallthrough=5',
'-Winit-self', '-Winit-self',

View File

@ -125,7 +125,7 @@ _systemd_analyze() {
elif __contains_word "$verb" ${VERBS[VERIFY]}; then elif __contains_word "$verb" ${VERBS[VERIFY]}; then
if [[ $cur = -* ]]; then if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --man=no --generators=yes --root --image' comps='--help --version --system --user --global --man=no --generators=yes --root --image --recursive-errors=no --recursive-errors=yes --recursive-errors=one'
else else
comps=$( compgen -A file -- "$cur" ) comps=$( compgen -A file -- "$cur" )
compopt -o filenames compopt -o filenames

View File

@ -89,6 +89,7 @@ _arguments \
'--global[Show global user instance config]' \ '--global[Show global user instance config]' \
'--root=[Add support for root argument]:PATH' \ '--root=[Add support for root argument]:PATH' \
'--image=[Add support for discrete images]:PATH' \ '--image=[Add support for discrete images]:PATH' \
'--recursive-errors=[When verifying a unit, control dependency verification]:MODE' \
'--no-pager[Do not pipe output into a pager]' \ '--no-pager[Do not pipe output into a pager]' \
'--man=[Do (not) check for existence of man pages]:boolean:(1 0)' \ '--man=[Do (not) check for existence of man pages]:boolean:(1 0)' \
'--order[When generating graph for dot, show only order]' \ '--order[When generating graph for dot, show only order]' \

View File

@ -11,10 +11,28 @@
#include "manager.h" #include "manager.h"
#include "pager.h" #include "pager.h"
#include "path-util.h" #include "path-util.h"
#include "string-table.h"
#include "strv.h" #include "strv.h"
#include "unit-name.h" #include "unit-name.h"
#include "unit-serialize.h" #include "unit-serialize.h"
static void log_syntax_callback(const char *unit, int level, void *userdata) {
Set **s = userdata;
int r;
assert(userdata);
assert(unit);
if (level > LOG_WARNING)
return;
r = set_put_strdup(s, unit);
if (r < 0) {
set_free_free(*s);
*s = POINTER_MAX;
}
}
static int prepare_filename(const char *filename, char **ret) { static int prepare_filename(const char *filename, char **ret) {
int r; int r;
const char *name; const char *name;
@ -218,13 +236,22 @@ static int verify_unit(Unit *u, bool check_man, const char *root) {
return r; return r;
} }
int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, const char *root) { static void set_destroy_ignore_pointer_max(Set** s) {
if (*s == POINTER_MAX)
return;
set_free_free(*s);
}
int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root) {
const ManagerTestRunFlags flags = const ManagerTestRunFlags flags =
MANAGER_TEST_RUN_MINIMAL | MANAGER_TEST_RUN_MINIMAL |
MANAGER_TEST_RUN_ENV_GENERATORS | MANAGER_TEST_RUN_ENV_GENERATORS |
(recursive_errors == RECURSIVE_ERRORS_NO) * MANAGER_TEST_RUN_IGNORE_DEPENDENCIES |
run_generators * MANAGER_TEST_RUN_GENERATORS; run_generators * MANAGER_TEST_RUN_GENERATORS;
_cleanup_(manager_freep) Manager *m = NULL; _cleanup_(manager_freep) Manager *m = NULL;
_cleanup_(set_destroy_ignore_pointer_max) Set *s = NULL;
_unused_ _cleanup_(clear_log_syntax_callback) dummy_t dummy;
Unit *units[strv_length(filenames)]; Unit *units[strv_length(filenames)];
_cleanup_free_ char *var = NULL; _cleanup_free_ char *var = NULL;
int r, k, i, count = 0; int r, k, i, count = 0;
@ -233,6 +260,11 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run
if (strv_isempty(filenames)) if (strv_isempty(filenames))
return 0; return 0;
/* Allow systemd-analyze to hook in a callback function so that it can get
* all the required log data from the function itself without having to rely
* on a global set variable for the same */
set_log_syntax_callback(log_syntax_callback, &s);
/* set the path */ /* set the path */
r = generate_path(&var, filenames); r = generate_path(&var, filenames);
if (r < 0) if (r < 0)
@ -283,5 +315,34 @@ int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run
r = k; r = k;
} }
return r; if (s == POINTER_MAX)
return log_oom();
if (set_isempty(s) || r != 0)
return r;
/* If all previous verifications succeeded, then either the recursive parsing of all the
* associated dependencies with RECURSIVE_ERRORS_YES or the parsing of the specified unit file
* with RECURSIVE_ERRORS_NO must have yielded a syntax warning and hence, a non-empty set. */
if (IN_SET(recursive_errors, RECURSIVE_ERRORS_YES, RECURSIVE_ERRORS_NO))
return -ENOTRECOVERABLE;
/* If all previous verifications succeeded, then the non-empty set could have resulted from
* a syntax warning encountered during the recursive parsing of the specified unit file and
* its direct dependencies. Hence, search for any of the filenames in the set and if found,
* return a non-zero process exit status. */
if (recursive_errors == RECURSIVE_ERRORS_ONE)
STRV_FOREACH(filename, filenames)
if (set_contains(s, basename(*filename)))
return -ENOTRECOVERABLE;
return 0;
} }
static const char* const recursive_errors_table[_RECURSIVE_ERRORS_MAX] = {
[RECURSIVE_ERRORS_NO] = "no",
[RECURSIVE_ERRORS_YES] = "yes",
[RECURSIVE_ERRORS_ONE] = "one",
};
DEFINE_STRING_TABLE_LOOKUP(recursive_errors, RecursiveErrors);

View File

@ -6,5 +6,16 @@
#include "execute.h" #include "execute.h"
#include "path-lookup.h" #include "path-lookup.h"
typedef enum RecursiveErrors {
RECURSIVE_ERRORS_YES, /* Look for errors in all associated units */
RECURSIVE_ERRORS_NO, /* Don't look for errors in any but the selected unit */
RECURSIVE_ERRORS_ONE, /* Look for errors in the selected unit and its direct dependencies */
_RECURSIVE_ERRORS_MAX,
_RECURSIVE_ERRORS_INVALID = -EINVAL,
} RecursiveErrors;
int verify_executable(Unit *u, const ExecCommand *exec, const char *root); int verify_executable(Unit *u, const ExecCommand *exec, const char *root);
int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, const char *root); int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root);
const char* recursive_errors_to_string(RecursiveErrors i) _const_;
RecursiveErrors recursive_errors_from_string(const char *s) _pure_;

View File

@ -46,6 +46,7 @@
#endif #endif
#include "sort-util.h" #include "sort-util.h"
#include "special.h" #include "special.h"
#include "string-table.h"
#include "strv.h" #include "strv.h"
#include "strxcpyx.h" #include "strxcpyx.h"
#include "terminal-util.h" #include "terminal-util.h"
@ -85,6 +86,7 @@ static PagerFlags arg_pager_flags = 0;
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static const char *arg_host = NULL; static const char *arg_host = NULL;
static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
static RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES;
static bool arg_man = true; static bool arg_man = true;
static bool arg_generators = false; static bool arg_generators = false;
static char *arg_root = NULL; static char *arg_root = NULL;
@ -2145,7 +2147,7 @@ static int do_condition(int argc, char *argv[], void *userdata) {
} }
static int do_verify(int argc, char *argv[], void *userdata) { static int do_verify(int argc, char *argv[], void *userdata) {
return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_root); return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root);
} }
static int do_security(int argc, char *argv[], void *userdata) { static int do_security(int argc, char *argv[], void *userdata) {
@ -2179,43 +2181,52 @@ static int help(int argc, char *argv[], void *userdata) {
printf("%s [OPTIONS...] COMMAND ...\n\n" printf("%s [OPTIONS...] COMMAND ...\n\n"
"%sProfile systemd, show unit dependencies, check unit files.%s\n" "%sProfile systemd, show unit dependencies, check unit files.%s\n"
"\nCommands:\n" "\nCommands:\n"
" [time] Print time required to boot the machine\n" " [time] Print time required to boot the machine\n"
" blame Print list of running units ordered by time to init\n" " blame Print list of running units ordered by\n"
" critical-chain [UNIT...] Print a tree of the time critical chain of units\n" " time to init\n"
" plot Output SVG graphic showing service initialization\n" " critical-chain [UNIT...] Print a tree of the time critical chain\n"
" dot [UNIT...] Output dependency graph in %s format\n" " of units\n"
" dump Output state serialization of service manager\n" " plot Output SVG graphic showing service\n"
" cat-config Show configuration file and drop-ins\n" " initialization\n"
" unit-files List files and symlinks for units\n" " dot [UNIT...] Output dependency graph in %s format\n"
" unit-paths List load directories for units\n" " dump Output state serialization of service\n"
" exit-status [STATUS...] List exit status definitions\n" " manager\n"
" capability [CAP...] List capability definitions\n" " cat-config Show configuration file and drop-ins\n"
" syscall-filter [NAME...] Print list of syscalls in seccomp filter\n" " unit-files List files and symlinks for units\n"
" condition CONDITION... Evaluate conditions and asserts\n" " unit-paths List load directories for units\n"
" verify FILE... Check unit files for correctness\n" " exit-status [STATUS...] List exit status definitions\n"
" calendar SPEC... Validate repetitive calendar time events\n" " capability [CAP...] List capability definitions\n"
" timestamp TIMESTAMP... Validate a timestamp\n" " syscall-filter [NAME...] Print list of syscalls in seccomp\n"
" timespan SPAN... Validate a time span\n" " filter\n"
" security [UNIT...] Analyze security of unit\n" " condition CONDITION... Evaluate conditions and asserts\n"
" verify FILE... Check unit files for correctness\n"
" calendar SPEC... Validate repetitive calendar time\n"
" events\n"
" timestamp TIMESTAMP... Validate a timestamp\n"
" timespan SPAN... Validate a time span\n"
" security [UNIT...] Analyze security of unit\n"
"\nOptions:\n" "\nOptions:\n"
" -h --help Show this help\n" " -h --help Show this help\n"
" --version Show package version\n" " --recursive-errors=MODE Control which units are verified\n"
" --no-pager Do not pipe output into a pager\n" " --version Show package version\n"
" --system Operate on system systemd instance\n" " --no-pager Do not pipe output into a pager\n"
" --user Operate on user systemd instance\n" " --system Operate on system systemd instance\n"
" --global Operate on global user configuration\n" " --user Operate on user systemd instance\n"
" -H --host=[USER@]HOST Operate on remote host\n" " --global Operate on global user configuration\n"
" -M --machine=CONTAINER Operate on local container\n" " -H --host=[USER@]HOST Operate on remote host\n"
" --order Show only order in the graph\n" " -M --machine=CONTAINER Operate on local container\n"
" --require Show only requirement in the graph\n" " --order Show only order in the graph\n"
" --from-pattern=GLOB Show only origins in the graph\n" " --require Show only requirement in the graph\n"
" --to-pattern=GLOB Show only destinations in the graph\n" " --from-pattern=GLOB Show only origins in the graph\n"
" --fuzz=SECONDS Also print services which finished SECONDS earlier\n" " --to-pattern=GLOB Show only destinations in the graph\n"
" than the latest in the branch\n" " --fuzz=SECONDS Also print services which finished SECONDS\n"
" --man[=BOOL] Do [not] check for existence of man pages\n" " earlier than the latest in the branch\n"
" --generators[=BOOL] Do [not] run unit generators (requires privileges)\n" " --man[=BOOL] Do [not] check for existence of man pages\n"
" --iterations=N Show the specified number of iterations\n" " --generators[=BOOL] Do [not] run unit generators\n"
" --base-time=TIMESTAMP Calculate calendar times relative to specified time\n" " (requires privileges)\n"
" --iterations=N Show the specified number of iterations\n"
" --base-time=TIMESTAMP Calculate calendar times relative to\n"
" specified time\n"
"\nSee the %s for details.\n", "\nSee the %s for details.\n",
program_invocation_short_name, program_invocation_short_name,
ansi_highlight(), ansi_highlight(),
@ -2247,28 +2258,30 @@ static int parse_argv(int argc, char *argv[]) {
ARG_GENERATORS, ARG_GENERATORS,
ARG_ITERATIONS, ARG_ITERATIONS,
ARG_BASE_TIME, ARG_BASE_TIME,
ARG_RECURSIVE_ERRORS,
}; };
static const struct option options[] = { static const struct option options[] = {
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION }, { "version", no_argument, NULL, ARG_VERSION },
{ "order", no_argument, NULL, ARG_ORDER }, { "order", no_argument, NULL, ARG_ORDER },
{ "require", no_argument, NULL, ARG_REQUIRE }, { "require", no_argument, NULL, ARG_REQUIRE },
{ "root", required_argument, NULL, ARG_ROOT }, { "root", required_argument, NULL, ARG_ROOT },
{ "image", required_argument, NULL, ARG_IMAGE }, { "image", required_argument, NULL, ARG_IMAGE },
{ "system", no_argument, NULL, ARG_SYSTEM }, { "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
{ "user", no_argument, NULL, ARG_USER }, { "system", no_argument, NULL, ARG_SYSTEM },
{ "global", no_argument, NULL, ARG_GLOBAL }, { "user", no_argument, NULL, ARG_USER },
{ "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN }, { "global", no_argument, NULL, ARG_GLOBAL },
{ "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN }, { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
{ "fuzz", required_argument, NULL, ARG_FUZZ }, { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "fuzz", required_argument, NULL, ARG_FUZZ },
{ "man", optional_argument, NULL, ARG_MAN }, { "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "generators", optional_argument, NULL, ARG_GENERATORS }, { "man", optional_argument, NULL, ARG_MAN },
{ "host", required_argument, NULL, 'H' }, { "generators", optional_argument, NULL, ARG_GENERATORS },
{ "machine", required_argument, NULL, 'M' }, { "host", required_argument, NULL, 'H' },
{ "iterations", required_argument, NULL, ARG_ITERATIONS }, { "machine", required_argument, NULL, 'M' },
{ "base-time", required_argument, NULL, ARG_BASE_TIME }, { "iterations", required_argument, NULL, ARG_ITERATIONS },
{ "base-time", required_argument, NULL, ARG_BASE_TIME },
{} {}
}; };
@ -2283,6 +2296,18 @@ static int parse_argv(int argc, char *argv[]) {
case 'h': case 'h':
return help(0, NULL, NULL); return help(0, NULL, NULL);
case ARG_RECURSIVE_ERRORS:
if (streq(optarg, "help")) {
DUMP_STRING_TABLE(recursive_errors, RecursiveErrors, _RECURSIVE_ERRORS_MAX);
return 0;
}
r = recursive_errors_from_string(optarg);
if (r < 0)
return log_error_errno(r, "Unknown mode passed to --recursive-errors='%s'.", optarg);
arg_recursive_errors = r;
break;
case ARG_VERSION: case ARG_VERSION:
return version(); return version();

View File

@ -39,6 +39,9 @@
#define SNDBUF_SIZE (8*1024*1024) #define SNDBUF_SIZE (8*1024*1024)
static log_syntax_callback_t log_syntax_callback = NULL;
static void *log_syntax_callback_userdata = NULL;
static LogTarget log_target = LOG_TARGET_CONSOLE; static LogTarget log_target = LOG_TARGET_CONSOLE;
static int log_max_level = LOG_INFO; static int log_max_level = LOG_INFO;
static int log_facility = LOG_DAEMON; static int log_facility = LOG_DAEMON;
@ -1341,6 +1344,14 @@ void log_received_signal(int level, const struct signalfd_siginfo *si) {
signal_to_string(si->ssi_signo)); signal_to_string(si->ssi_signo));
} }
void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata) {
assert(!log_syntax_callback || !cb);
assert(!log_syntax_callback_userdata || !userdata);
log_syntax_callback = cb;
log_syntax_callback_userdata = userdata;
}
int log_syntax_internal( int log_syntax_internal(
const char *unit, const char *unit,
int level, int level,
@ -1352,6 +1363,9 @@ int log_syntax_internal(
const char *func, const char *func,
const char *format, ...) { const char *format, ...) {
if (log_syntax_callback)
log_syntax_callback(unit, level, log_syntax_callback_userdata);
PROTECT_ERRNO; PROTECT_ERRNO;
char buffer[LINE_MAX]; char buffer[LINE_MAX];
va_list ap; va_list ap;

View File

@ -32,6 +32,15 @@ typedef enum LogTarget{
#define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1) #define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1)
#define ERRNO_VALUE(val) (abs(val) & 255) #define ERRNO_VALUE(val) (abs(val) & 255)
/* The callback function to be invoked when syntax warnings are seen
* in the unit files. */
typedef void (*log_syntax_callback_t)(const char *unit, int level, void *userdata);
void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata);
static inline void clear_log_syntax_callback(dummy_t *dummy) {
set_log_syntax_callback(/* cb= */ NULL, /* userdata= */ NULL);
}
const char *log_target_to_string(LogTarget target) _const_; const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_; LogTarget log_target_from_string(const char *s) _pure_;
void log_set_target(LogTarget target); void log_set_target(LogTarget target);

View File

@ -23,28 +23,12 @@
#define _packed_ __attribute__((__packed__)) #define _packed_ __attribute__((__packed__))
#define _malloc_ __attribute__((__malloc__)) #define _malloc_ __attribute__((__malloc__))
#define _weak_ __attribute__((__weak__)) #define _weak_ __attribute__((__weak__))
#define _likely_(x) (__builtin_expect(!!(x), 1))
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
#define _public_ __attribute__((__visibility__("default"))) #define _public_ __attribute__((__visibility__("default")))
#define _hidden_ __attribute__((__visibility__("hidden"))) #define _hidden_ __attribute__((__visibility__("hidden")))
#define _weakref_(x) __attribute__((__weakref__(#x))) #define _weakref_(x) __attribute__((__weakref__(#x)))
#define _alignas_(x) __attribute__((__aligned__(__alignof(x)))) #define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
#define _alignptr_ __attribute__((__aligned__(sizeof(void*)))) #define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
#define _warn_unused_result_ __attribute__((__warn_unused_result__)) #define _warn_unused_result_ __attribute__((__warn_unused_result__))
#if __GNUC__ >= 7
#define _fallthrough_ __attribute__((__fallthrough__))
#else
#define _fallthrough_
#endif
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
* compiler versions */
#ifndef _noreturn_
#if __STDC_VERSION__ >= 201112L
#define _noreturn_ _Noreturn
#else
#define _noreturn_ __attribute__((__noreturn__))
#endif
#endif
#if !defined(HAS_FEATURE_MEMORY_SANITIZER) #if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# if defined(__has_feature) # if defined(__has_feature)
@ -209,13 +193,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
return m; return m;
} }
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
* Contrary to strlen(), this is a constant expression.
* @x: a string literal.
*/
#define STRLEN(x) (sizeof(""x"") - 1U)
/* /*
* container_of - cast a member of a structure out to the containing structure * container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member. * @ptr: the pointer to the member.
@ -483,4 +460,10 @@ static inline size_t size_add(size_t x, size_t y) {
return y >= SIZE_MAX - x ? SIZE_MAX : x + y; return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
} }
typedef struct {
int _empty[0];
} dummy_t;
assert_cc(sizeof(dummy_t) == 0);
#include "log.h" #include "log.h"

15
src/boot/efi/assert.c Normal file
View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#ifndef NDEBUG
#include <efi.h>
#include <efilib.h>
#include "util.h"
void efi_assert(const char *expr, const char *file, unsigned line, const char *function) {
log_error_stall(L"systemd-boot assertion '%a' failed at %a:%u, function %a(). Halting.", expr, file, line, function);
for (;;)
uefi_call_wrapper(BS->Stall, 1, 60 * 1000 * 1000);
}
#endif

View File

@ -72,6 +72,9 @@ typedef struct {
} Config; } Config;
static VOID cursor_left(UINTN *cursor, UINTN *first) { static VOID cursor_left(UINTN *cursor, UINTN *first) {
assert(cursor);
assert(first);
if ((*cursor) > 0) if ((*cursor) > 0)
(*cursor)--; (*cursor)--;
else if ((*first) > 0) else if ((*first) > 0)
@ -84,6 +87,9 @@ static VOID cursor_right(
UINTN x_max, UINTN x_max,
UINTN len) { UINTN len) {
assert(cursor);
assert(first);
if ((*cursor)+1 < x_max) if ((*cursor)+1 < x_max)
(*cursor)++; (*cursor)++;
else if ((*first) + (*cursor) < len) else if ((*first) + (*cursor) < len)
@ -100,6 +106,8 @@ static BOOLEAN line_edit(
UINTN size, len, first, cursor, clear; UINTN size, len, first, cursor, clear;
BOOLEAN exit, enter; BOOLEAN exit, enter;
assert(line_out);
if (!line_in) if (!line_in)
line_in = L""; line_in = L"";
size = StrLen(line_in) + 1024; size = StrLen(line_in) + 1024;
@ -134,7 +142,7 @@ static BOOLEAN line_edit(
uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print); uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, print);
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos); uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, cursor, y_pos);
err = console_key_read(&key, TRUE); err = console_key_read(&key, 0);
if (EFI_ERROR(err)) if (EFI_ERROR(err))
continue; continue;
@ -332,6 +340,8 @@ static BOOLEAN line_edit(
} }
static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) { static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) {
assert(config);
if (key == 0) if (key == 0)
return -1; return -1;
@ -362,6 +372,9 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
_cleanup_freepool_ CHAR16 *partstr = NULL, *defaultstr = NULL; _cleanup_freepool_ CHAR16 *partstr = NULL, *defaultstr = NULL;
UINTN x, y; UINTN x, y;
assert(config);
assert(loaded_image_path);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK);
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@ -387,7 +400,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
Print(L"OsIndicationsSupported: %d\n", indvar); Print(L"OsIndicationsSupported: %d\n", indvar);
Print(L"\n--- press key ---\n\n"); Print(L"\n--- press key ---\n\n");
console_key_read(&key, TRUE); console_key_read(&key, 0);
Print(L"timeout: %u\n", config->timeout_sec); Print(L"timeout: %u\n", config->timeout_sec);
if (config->timeout_sec_efivar >= 0) if (config->timeout_sec_efivar >= 0)
@ -432,7 +445,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
Print(L"LoaderEntryDefault: %s\n", defaultstr); Print(L"LoaderEntryDefault: %s\n", defaultstr);
Print(L"\n--- press key ---\n\n"); Print(L"\n--- press key ---\n\n");
console_key_read(&key, TRUE); console_key_read(&key, 0);
for (UINTN i = 0; i < config->entry_count; i++) { for (UINTN i = 0; i < config->entry_count; i++) {
ConfigEntry *entry; ConfigEntry *entry;
@ -482,7 +495,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) {
entry->path, entry->next_name); entry->path, entry->next_name);
Print(L"\n--- press key ---\n\n"); Print(L"\n--- press key ---\n\n");
console_key_read(&key, TRUE); console_key_read(&key, 0);
} }
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@ -493,6 +506,10 @@ static BOOLEAN menu_run(
ConfigEntry **chosen_entry, ConfigEntry **chosen_entry,
CHAR16 *loaded_image_path) { CHAR16 *loaded_image_path) {
assert(config);
assert(chosen_entry);
assert(loaded_image_path);
EFI_STATUS err; EFI_STATUS err;
UINTN visible_max; UINTN visible_max;
UINTN idx_highlight; UINTN idx_highlight;
@ -509,11 +526,10 @@ static BOOLEAN menu_run(
UINTN y_max; UINTN y_max;
CHAR16 *status; CHAR16 *status;
CHAR16 *clearline; CHAR16 *clearline;
INTN timeout_remain; UINTN timeout_remain = config->timeout_sec;
INT16 idx; INT16 idx;
BOOLEAN exit = FALSE; BOOLEAN exit = FALSE;
BOOLEAN run = TRUE; BOOLEAN run = TRUE;
BOOLEAN wait = FALSE;
graphics_mode(FALSE); graphics_mode(FALSE);
uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE); uefi_call_wrapper(ST->ConIn->Reset, 2, ST->ConIn, FALSE);
@ -527,7 +543,7 @@ static BOOLEAN menu_run(
err = console_set_mode(&config->console_mode, config->console_mode_change); err = console_set_mode(&config->console_mode, config->console_mode_change);
if (EFI_ERROR(err)) { if (EFI_ERROR(err)) {
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
Print(L"Error switching console mode to %ld: %r.\r", (UINT64)config->console_mode, err); log_error_stall(L"Error switching console mode to %lu: %r", (UINT64)config->console_mode, err);
} }
} else } else
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
@ -538,12 +554,6 @@ static BOOLEAN menu_run(
y_max = 25; y_max = 25;
} }
/* we check 10 times per second for a keystroke */
if (config->timeout_sec > 0)
timeout_remain = config->timeout_sec * 10;
else
timeout_remain = -1;
idx_highlight = config->idx_default; idx_highlight = config->idx_default;
idx_highlight_prev = 0; idx_highlight_prev = 0;
@ -643,7 +653,7 @@ static BOOLEAN menu_run(
if (timeout_remain > 0) { if (timeout_remain > 0) {
FreePool(status); FreePool(status);
status = PoolPrint(L"Boot in %d sec.", (timeout_remain + 5) / 10); status = PoolPrint(L"Boot in %d s.", timeout_remain);
} }
/* print status at last line of screen */ /* print status at last line of screen */
@ -664,27 +674,18 @@ static BOOLEAN menu_run(
uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len); uefi_call_wrapper(ST->ConOut->OutputString, 2, ST->ConOut, clearline+1 + x + len);
} }
err = console_key_read(&key, wait); err = console_key_read(&key, timeout_remain > 0 ? 1000 * 1000 : 0);
if (EFI_ERROR(err)) { if (err == EFI_TIMEOUT) {
/* timeout reached */ timeout_remain--;
if (timeout_remain == 0) { if (timeout_remain == 0) {
exit = TRUE; exit = TRUE;
break; break;
} }
/* sleep and update status */ /* update status */
if (timeout_remain > 0) {
uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
timeout_remain--;
continue;
}
/* timeout disabled, wait for next key */
wait = TRUE;
continue; continue;
} } else
timeout_remain = 0;
timeout_remain = -1;
/* clear status after keystroke */ /* clear status after keystroke */
if (status) { if (status) {
@ -787,7 +788,7 @@ static BOOLEAN menu_run(
config->timeout_sec_efivar, config->timeout_sec_efivar,
EFI_VARIABLE_NON_VOLATILE); EFI_VARIABLE_NON_VOLATILE);
if (config->timeout_sec_efivar > 0) if (config->timeout_sec_efivar > 0)
status = PoolPrint(L"Menu timeout set to %d sec.", config->timeout_sec_efivar); status = PoolPrint(L"Menu timeout set to %d s.", config->timeout_sec_efivar);
else else
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu."); status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
} else if (config->timeout_sec_efivar <= 0){ } else if (config->timeout_sec_efivar <= 0){
@ -795,7 +796,7 @@ static BOOLEAN menu_run(
efivar_set( efivar_set(
LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE); LOADER_GUID, L"LoaderConfigTimeout", NULL, EFI_VARIABLE_NON_VOLATILE);
if (config->timeout_sec_config > 0) if (config->timeout_sec_config > 0)
status = PoolPrint(L"Menu timeout of %d sec is defined by configuration file.", status = PoolPrint(L"Menu timeout of %d s is defined by configuration file.",
config->timeout_sec_config); config->timeout_sec_config);
else else
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu."); status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
@ -813,7 +814,7 @@ static BOOLEAN menu_run(
config->timeout_sec_efivar, config->timeout_sec_efivar,
EFI_VARIABLE_NON_VOLATILE); EFI_VARIABLE_NON_VOLATILE);
if (config->timeout_sec_efivar > 0) if (config->timeout_sec_efivar > 0)
status = PoolPrint(L"Menu timeout set to %d sec.", status = PoolPrint(L"Menu timeout set to %d s.",
config->timeout_sec_efivar); config->timeout_sec_efivar);
else else
status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu."); status = StrDuplicate(L"Menu disabled. Hold down key at bootup to show menu.");
@ -884,6 +885,9 @@ static BOOLEAN menu_run(
} }
static VOID config_add_entry(Config *config, ConfigEntry *entry) { static VOID config_add_entry(Config *config, ConfigEntry *entry) {
assert(config);
assert(entry);
if ((config->entry_count & 15) == 0) { if ((config->entry_count & 15) == 0) {
UINTN i; UINTN i;
@ -924,6 +928,12 @@ static CHAR8 *line_get_key_value(
CHAR8 *line, *value; CHAR8 *line, *value;
UINTN linelen; UINTN linelen;
assert(content);
assert(sep);
assert(pos);
assert(key_ret);
assert(value_ret);
skip: skip:
line = content + *pos; line = content + *pos;
if (*line == '\0') if (*line == '\0')
@ -985,6 +995,10 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
CHAR8 *line; CHAR8 *line;
UINTN pos = 0; UINTN pos = 0;
CHAR8 *key, *value; CHAR8 *key, *value;
EFI_STATUS err;
assert(config);
assert(content);
while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) { while ((line = line_get_key_value(content, (CHAR8 *)" \t", &pos, &key, &value))) {
if (strcmpa((CHAR8 *)"timeout", key) == 0) { if (strcmpa((CHAR8 *)"timeout", key) == 0) {
@ -1004,32 +1018,23 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
} }
if (strcmpa((CHAR8 *)"editor", key) == 0) { if (strcmpa((CHAR8 *)"editor", key) == 0) {
BOOLEAN on; err = parse_boolean(value, &config->editor);
if (EFI_ERROR(err))
if (EFI_ERROR(parse_boolean(value, &on))) log_error_stall(L"Error parsing 'editor' config option: %a", value);
continue;
config->editor = on;
continue; continue;
} }
if (strcmpa((CHAR8 *)"auto-entries", key) == 0) { if (strcmpa((CHAR8 *)"auto-entries", key) == 0) {
BOOLEAN on; err = parse_boolean(value, &config->auto_entries);
if (EFI_ERROR(err))
if (EFI_ERROR(parse_boolean(value, &on))) log_error_stall(L"Error parsing 'auto-entries' config option: %a", value);
continue;
config->auto_entries = on;
continue; continue;
} }
if (strcmpa((CHAR8 *)"auto-firmware", key) == 0) { if (strcmpa((CHAR8 *)"auto-firmware", key) == 0) {
BOOLEAN on; err = parse_boolean(value, &config->auto_firmware);
if (EFI_ERROR(err))
if (EFI_ERROR(parse_boolean(value, &on))) log_error_stall(L"Error parsing 'auto-firmware' config option: %a", value);
continue;
config->auto_firmware = on;
continue; continue;
} }
@ -1061,8 +1066,11 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
else { else {
BOOLEAN on; BOOLEAN on;
if (EFI_ERROR(parse_boolean(value, &on))) err = parse_boolean(value, &on);
if (EFI_ERROR(err)) {
log_error_stall(L"Error parsing 'random-seed-mode' config option: %a", value);
continue; continue;
}
config->random_seed_mode = on ? RANDOM_SEED_ALWAYS : RANDOM_SEED_OFF; config->random_seed_mode = on ? RANDOM_SEED_ALWAYS : RANDOM_SEED_OFF;
} }
@ -1079,6 +1087,10 @@ static VOID config_entry_parse_tries(
UINTN left = UINTN_MAX, done = UINTN_MAX, factor = 1, i, next_left, next_done; UINTN left = UINTN_MAX, done = UINTN_MAX, factor = 1, i, next_left, next_done;
_cleanup_freepool_ CHAR16 *prefix = NULL; _cleanup_freepool_ CHAR16 *prefix = NULL;
assert(entry);
assert(path);
assert(file);
/* /*
* Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the * Parses a suffix of two counters (one going down, one going up) in the form "+LEFT-DONE" from the end of the
* filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in * filename (but before the .efi/.conf suffix), where the "-DONE" part is optional and may be left out (in
@ -1196,6 +1208,9 @@ static VOID config_entry_bump_counters(
UINTN file_info_size, a, b; UINTN file_info_size, a, b;
EFI_STATUS r; EFI_STATUS r;
assert(entry);
assert(root_dir);
if (entry->tries_left == UINTN_MAX) if (entry->tries_left == UINTN_MAX)
return; return;
@ -1221,8 +1236,7 @@ static VOID config_entry_bump_counters(
break; break;
if (r != EFI_BUFFER_TOO_SMALL || file_info_size * 2 < file_info_size) { if (r != EFI_BUFFER_TOO_SMALL || file_info_size * 2 < file_info_size) {
Print(L"\nFailed to get file info for '%s': %r\n", old_path, r); log_error_stall(L"Failed to get file info for '%s': %r", old_path, r);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return; return;
} }
@ -1234,8 +1248,7 @@ static VOID config_entry_bump_counters(
StrCpy(file_info->FileName, entry->next_name); StrCpy(file_info->FileName, entry->next_name);
r = uefi_call_wrapper(handle->SetInfo, 4, handle, &EfiFileInfoGuid, file_info_size, file_info); r = uefi_call_wrapper(handle->SetInfo, 4, handle, &EfiFileInfoGuid, file_info_size, file_info);
if (EFI_ERROR(r)) { if (EFI_ERROR(r)) {
Print(L"\nFailed to rename '%s' to '%s', ignoring: %r\n", old_path, entry->next_name, r); log_error_stall(L"Failed to rename '%s' to '%s', ignoring: %r", old_path, entry->next_name, r);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return; return;
} }
@ -1271,6 +1284,14 @@ static VOID config_entry_add_from_file(
EFI_FILE_HANDLE handle; EFI_FILE_HANDLE handle;
_cleanup_freepool_ CHAR16 *initrd = NULL; _cleanup_freepool_ CHAR16 *initrd = NULL;
assert(config);
assert(device);
assert(root_dir);
assert(path);
assert(file);
assert(content);
assert(loaded_image_path);
entry = AllocatePool(sizeof(ConfigEntry)); entry = AllocatePool(sizeof(ConfigEntry));
*entry = (ConfigEntry) { *entry = (ConfigEntry) {
@ -1399,6 +1420,8 @@ static VOID config_load_defaults(Config *config, EFI_FILE *root_dir) {
UINTN sec; UINTN sec;
EFI_STATUS err; EFI_STATUS err;
assert(root_dir);
*config = (Config) { *config = (Config) {
.editor = TRUE, .editor = TRUE,
.auto_entries = TRUE, .auto_entries = TRUE,
@ -1436,6 +1459,11 @@ static VOID config_load_entries(
EFI_FILE_HANDLE entries_dir; EFI_FILE_HANDLE entries_dir;
EFI_STATUS err; EFI_STATUS err;
assert(config);
assert(device);
assert(root_dir);
assert(loaded_image_path);
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, (CHAR16*) L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL); err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &entries_dir, (CHAR16*) L"\\loader\\entries", EFI_FILE_MODE_READ, 0ULL);
if (!EFI_ERROR(err)) { if (!EFI_ERROR(err)) {
for (;;) { for (;;) {
@ -1471,6 +1499,9 @@ static VOID config_load_entries(
static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) { static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
INTN r; INTN r;
assert(a);
assert(b);
/* Order entries that have no tries left to the beginning of the list */ /* Order entries that have no tries left to the beginning of the list */
if (a->tries_left != 0 && b->tries_left == 0) if (a->tries_left != 0 && b->tries_left == 0)
return 1; return 1;
@ -1501,6 +1532,8 @@ static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
} }
static VOID config_sort_entries(Config *config) { static VOID config_sort_entries(Config *config) {
assert(config);
for (UINTN i = 1; i < config->entry_count; i++) { for (UINTN i = 1; i < config->entry_count; i++) {
BOOLEAN more; BOOLEAN more;
@ -1522,6 +1555,9 @@ static VOID config_sort_entries(Config *config) {
} }
static INTN config_entry_find(Config *config, CHAR16 *id) { static INTN config_entry_find(Config *config, CHAR16 *id) {
assert(config);
assert(id);
for (UINTN i = 0; i < config->entry_count; i++) for (UINTN i = 0; i < config->entry_count; i++)
if (StrCmp(config->entries[i]->id, id) == 0) if (StrCmp(config->entries[i]->id, id) == 0)
return (INTN) i; return (INTN) i;
@ -1534,6 +1570,8 @@ static VOID config_default_entry_select(Config *config) {
EFI_STATUS err; EFI_STATUS err;
INTN i; INTN i;
assert(config);
/* /*
* The EFI variable to specify a boot entry for the next, and only the * The EFI variable to specify a boot entry for the next, and only the
* next reboot. The variable is always cleared directly after it is read. * next reboot. The variable is always cleared directly after it is read.
@ -1604,6 +1642,8 @@ static VOID config_default_entry_select(Config *config) {
static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) { static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) {
BOOLEAN non_unique = FALSE; BOOLEAN non_unique = FALSE;
assert(entries);
for (UINTN i = 0; i < entry_count; i++) for (UINTN i = 0; i < entry_count; i++)
entries[i]->non_unique = FALSE; entries[i]->non_unique = FALSE;
@ -1622,6 +1662,8 @@ static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) {
/* generate a unique title, avoiding non-distinguishable menu entries */ /* generate a unique title, avoiding non-distinguishable menu entries */
static VOID config_title_generate(Config *config) { static VOID config_title_generate(Config *config) {
assert(config);
/* set title */ /* set title */
for (UINTN i = 0; i < config->entry_count; i++) { for (UINTN i = 0; i < config->entry_count; i++) {
CHAR16 *title; CHAR16 *title;
@ -1694,6 +1736,11 @@ static BOOLEAN config_entry_add_call(
ConfigEntry *entry; ConfigEntry *entry;
assert(config);
assert(id);
assert(title);
assert(call);
entry = AllocatePool(sizeof(ConfigEntry)); entry = AllocatePool(sizeof(ConfigEntry));
*entry = (ConfigEntry) { *entry = (ConfigEntry) {
.id = StrDuplicate(id), .id = StrDuplicate(id),
@ -1720,11 +1767,17 @@ static ConfigEntry *config_entry_add_loader(
ConfigEntry *entry; ConfigEntry *entry;
assert(config);
assert(device);
assert(id);
assert(title);
assert(loader);
entry = AllocatePool(sizeof(ConfigEntry)); entry = AllocatePool(sizeof(ConfigEntry));
*entry = (ConfigEntry) { *entry = (ConfigEntry) {
.type = type, .type = type,
.title = StrDuplicate(title), .title = StrDuplicate(title),
.version = StrDuplicate(version), .version = version ? StrDuplicate(version) : NULL,
.device = device, .device = device,
.loader = StrDuplicate(loader), .loader = StrDuplicate(loader),
.id = StrDuplicate(id), .id = StrDuplicate(id),
@ -1753,6 +1806,13 @@ static BOOLEAN config_entry_add_loader_auto(
ConfigEntry *entry; ConfigEntry *entry;
EFI_STATUS err; EFI_STATUS err;
assert(config);
assert(device);
assert(root_dir);
assert(id);
assert(title);
assert(loader);
if (!config->auto_entries) if (!config->auto_entries)
return FALSE; return FALSE;
@ -1793,6 +1853,8 @@ static VOID config_entry_add_osx(Config *config) {
UINTN handle_count = 0; UINTN handle_count = 0;
_cleanup_freepool_ EFI_HANDLE *handles = NULL; _cleanup_freepool_ EFI_HANDLE *handles = NULL;
assert(config);
if (!config->auto_entries) if (!config->auto_entries)
return; return;
@ -1823,6 +1885,10 @@ static VOID config_entry_add_linux(
EFI_STATUS err; EFI_STATUS err;
ConfigEntry *entry; ConfigEntry *entry;
assert(config);
assert(device);
assert(root_dir);
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, (CHAR16*) L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL); err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &linux_dir, (CHAR16*) L"\\EFI\\Linux", EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err)) if (EFI_ERROR(err))
return; return;
@ -1957,6 +2023,9 @@ static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node
EFI_DEVICE_PATH *parent; EFI_DEVICE_PATH *parent;
UINTN len; UINTN len;
assert(path);
assert(node);
len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path; len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path;
parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH)); parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH));
CopyMem(parent, path, len); CopyMem(parent, path, len);
@ -1978,6 +2047,9 @@ static VOID config_load_xbootldr(
EFI_FILE *root_dir; EFI_FILE *root_dir;
EFI_STATUS r; EFI_STATUS r;
assert(config);
assert(device);
partition_path = DevicePathFromHandle(device); partition_path = DevicePathFromHandle(device);
if (!partition_path) if (!partition_path)
return; return;
@ -2164,19 +2236,16 @@ static EFI_STATUS image_start(
CHAR16 *options; CHAR16 *options;
EFI_STATUS err; EFI_STATUS err;
assert(config);
assert(entry);
path = FileDevicePath(entry->device, entry->loader); path = FileDevicePath(entry->device, entry->loader);
if (!path) { if (!path)
Print(L"Error getting device path."); return log_error_status_stall(EFI_INVALID_PARAMETER, L"Error getting device path.");
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return EFI_INVALID_PARAMETER;
}
err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image); err = uefi_call_wrapper(BS->LoadImage, 6, FALSE, parent_image, path, NULL, 0, &image);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Error loading %s: %r", entry->loader, err); return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
}
if (config->options_edit) if (config->options_edit)
options = config->options_edit; options = config->options_edit;
@ -2190,22 +2259,19 @@ static EFI_STATUS image_start(
err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err)) { if (EFI_ERROR(err)) {
Print(L"Error getting LoadedImageProtocol handle: %r", err); log_error_stall(L"Error getting LoadedImageProtocol handle: %r", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
goto out_unload; goto out_unload;
} }
loaded_image->LoadOptions = options; loaded_image->LoadOptions = options;
loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16); loaded_image->LoadOptionsSize = StrSize(loaded_image->LoadOptions);
#if ENABLE_TPM #if ENABLE_TPM
/* Try to log any options to the TPM, especially to catch manually edited options */ /* Try to log any options to the TPM, especially to catch manually edited options */
err = tpm_log_event(SD_TPM_PCR, err = tpm_log_event(SD_TPM_PCR,
(EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions, (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
loaded_image->LoadOptionsSize, loaded_image->LoadOptions); loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Unable to add image options measurement: %r", err); log_error_stall(L"Unable to add image options measurement: %r", err);
uefi_call_wrapper(BS->Stall, 1, 200 * 1000);
}
#endif #endif
} }
@ -2231,12 +2297,11 @@ static EFI_STATUS reboot_into_firmware(VOID) {
return err; return err;
err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL); err = uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, EFI_SUCCESS, 0, NULL);
Print(L"Error calling ResetSystem: %r", err); return log_error_status_stall(err, L"Error calling ResetSystem: %r", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
} }
static VOID config_free(Config *config) { static VOID config_free(Config *config) {
assert(config);
for (UINTN i = 0; i < config->entry_count; i++) for (UINTN i = 0; i < config->entry_count; i++)
config_entry_free(config->entries[i]); config_entry_free(config->entries[i]);
FreePool(config->entries); FreePool(config->entries);
@ -2246,26 +2311,30 @@ static VOID config_free(Config *config) {
} }
static VOID config_write_entries_to_variable(Config *config) { static VOID config_write_entries_to_variable(Config *config) {
_cleanup_freepool_ CHAR16 *buffer = NULL; _cleanup_freepool_ CHAR8 *buffer = NULL;
UINTN sz = 0; UINTN sz = 0;
CHAR16 *p; CHAR8 *p;
assert(config);
for (UINTN i = 0; i < config->entry_count; i++) for (UINTN i = 0; i < config->entry_count; i++)
sz += StrLen(config->entries[i]->id) + 1; sz += StrSize(config->entries[i]->id);
p = buffer = AllocatePool(sz * sizeof(CHAR16)); p = buffer = AllocatePool(sz);
for (UINTN i = 0; i < config->entry_count; i++) { for (UINTN i = 0; i < config->entry_count; i++) {
UINTN l; UINTN l;
l = StrLen(config->entries[i]->id) + 1; l = StrSize(config->entries[i]->id);
CopyMem(p, config->entries[i]->id, l * sizeof(CHAR16)); CopyMem(p, config->entries[i]->id, l);
p += l; p += l;
} }
assert(p == buffer + sz);
/* Store the full list of discovered entries. */ /* Store the full list of discovered entries. */
(void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, (UINT8 *) p - (UINT8 *) buffer, 0); (void) efivar_set_raw(LOADER_GUID, L"LoaderEntries", buffer, sz, 0);
} }
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
@ -2305,30 +2374,21 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Error getting a LoadedImageProtocol handle: %r", err); return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
}
/* export the device path this image is started from */ /* export the device path this image is started from */
if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS)
efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0); efivar_set(LOADER_GUID, L"LoaderDevicePartUUID", uuid, 0);
root_dir = LibOpenRoot(loaded_image->DeviceHandle); root_dir = LibOpenRoot(loaded_image->DeviceHandle);
if (!root_dir) { if (!root_dir)
Print(L"Unable to open root directory."); return log_error_status_stall(EFI_LOAD_ERROR, L"Unable to open root directory.", EFI_LOAD_ERROR);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return EFI_LOAD_ERROR;
}
if (secure_boot_enabled() && shim_loaded()) { if (secure_boot_enabled() && shim_loaded()) {
err = security_policy_install(); err = security_policy_install();
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Error installing security policy: %r ", err); return log_error_status_stall(err, L"Error installing security policy: %r", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
}
} }
/* the filesystem path to this image, to prevent adding ourselves to the menu */ /* the filesystem path to this image, to prevent adding ourselves to the menu */
@ -2367,8 +2427,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
} }
if (config.entry_count == 0) { if (config.entry_count == 0) {
Print(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed."); log_error_stall(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
goto out; goto out;
} }
@ -2392,13 +2451,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
else { else {
UINT64 key; UINT64 key;
err = console_key_read(&key, FALSE); /* Block up to 100ms to give firmware time to get input working. */
err = console_key_read(&key, 100 * 1000);
if (err == EFI_NOT_READY) {
uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
err = console_key_read(&key, FALSE);
}
if (!EFI_ERROR(err)) { if (!EFI_ERROR(err)) {
INT16 idx; INT16 idx;
@ -2440,8 +2494,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = image_start(image, &config, entry); err = image_start(image, &config, entry);
if (EFI_ERROR(err)) { if (EFI_ERROR(err)) {
graphics_mode(FALSE); graphics_mode(FALSE);
Print(L"\nFailed to execute %s (%s): %r\n", entry->title, entry->loader, err); log_error_stall(L"Failed to execute %s (%s): %r", entry->title, entry->loader, err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
goto out; goto out;
} }

View File

@ -11,61 +11,107 @@
#define EFI_SIMPLE_TEXT_INPUT_EX_GUID &(EFI_GUID) EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID #define EFI_SIMPLE_TEXT_INPUT_EX_GUID &(EFI_GUID) EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID
EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait) { static inline void EventClosep(EFI_EVENT *event) {
if (!*event)
return;
uefi_call_wrapper(BS->CloseEvent, 1, *event);
}
/*
* Reading input from the console sounds like an easy task to do, but thanks to broken
* firmware it is actually a nightmare.
*
* There is a ConIn and TextInputEx API for this. Ideally we want to use TextInputEx,
* because that gives us Ctrl/Alt/Shift key state information. Unfortunately, it is not
* always available and sometimes just non-functional.
*
* On the other hand we have ConIn, where some firmware likes to just freeze on us
* if we call ReadKeyStroke on it.
*
* Therefore, we use WaitForEvent on both ConIn and TextInputEx (if available) along
* with a timer event. The timer ensures there is no need to call into functions
* that might freeze on us, while still allowing us to show a timeout counter.
*/
EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec) {
static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx; static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInputEx;
static BOOLEAN checked; static BOOLEAN checked;
UINTN index; UINTN index;
EFI_INPUT_KEY k; EFI_INPUT_KEY k;
EFI_STATUS err; EFI_STATUS err;
_cleanup_(EventClosep) EFI_EVENT timer = NULL;
EFI_EVENT events[3] = { ST->ConIn->WaitForKey };
UINTN n_events = 1;
assert(key);
if (!checked) { if (!checked) {
err = LibLocateProtocol(EFI_SIMPLE_TEXT_INPUT_EX_GUID, (VOID **)&TextInputEx); err = LibLocateProtocol(EFI_SIMPLE_TEXT_INPUT_EX_GUID, (VOID **)&TextInputEx);
if (EFI_ERROR(err)) if (EFI_ERROR(err) ||
uefi_call_wrapper(BS->CheckEvent, 1, TextInputEx->WaitForKeyEx) == EFI_INVALID_PARAMETER)
/* If WaitForKeyEx fails here, the firmware pretends it talks this
* protocol, but it really doesn't. */
TextInputEx = NULL; TextInputEx = NULL;
else
events[n_events++] = TextInputEx->WaitForKeyEx;
checked = TRUE; checked = TRUE;
} }
/* wait until key is pressed */ if (timeout_usec > 0) {
if (wait) err = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL, &timer);
uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &index); if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error creating timer event: %r", err);
if (TextInputEx) { /* SetTimer expects 100ns units for some reason. */
EFI_KEY_DATA keydata; err = uefi_call_wrapper(BS->SetTimer, 3, timer, TimerRelative, timeout_usec * 10);
UINT64 keypress; if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error arming timer event: %r", err);
err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata); events[n_events++] = timer;
if (!EFI_ERROR(err)) { }
UINT32 shift = 0;
err = uefi_call_wrapper(BS->WaitForEvent, 3, n_events, events, &index);
/* do not distinguish between left and right keys */ if (EFI_ERROR(err))
if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) { return log_error_status_stall(err, L"Error waiting for events: %r", err);
if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
shift |= EFI_CONTROL_PRESSED; if (timeout_usec > 0 && timer == events[index])
if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED)) return EFI_TIMEOUT;
shift |= EFI_ALT_PRESSED;
}; /* TextInputEx might be ready too even if ConIn got to signal first. */
if (TextInputEx && !EFI_ERROR(uefi_call_wrapper(BS->CheckEvent, 1, TextInputEx->WaitForKeyEx))) {
/* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */ EFI_KEY_DATA keydata;
keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar); UINT64 keypress;
if (keypress > 0) { UINT32 shift = 0;
*key = keypress;
return 0; err = uefi_call_wrapper(TextInputEx->ReadKeyStrokeEx, 2, TextInputEx, &keydata);
} if (EFI_ERROR(err))
} return err;
/* do not distinguish between left and right keys */
if (keydata.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) {
if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_CONTROL_PRESSED|EFI_LEFT_CONTROL_PRESSED))
shift |= EFI_CONTROL_PRESSED;
if (keydata.KeyState.KeyShiftState & (EFI_RIGHT_ALT_PRESSED|EFI_LEFT_ALT_PRESSED))
shift |= EFI_ALT_PRESSED;
};
/* 32 bit modifier keys + 16 bit scan code + 16 bit unicode */
keypress = KEYPRESS(shift, keydata.Key.ScanCode, keydata.Key.UnicodeChar);
if (keypress > 0) {
*key = keypress;
return EFI_SUCCESS;
}
return EFI_NOT_READY;
} }
/* fallback for firmware which does not support SimpleTextInputExProtocol
*
* This is also called in case ReadKeyStrokeEx did not return a key, because
* some broken firmwares offer SimpleTextInputExProtocol, but never actually
* handle any key. */
err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k); err = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &k);
if (EFI_ERROR(err)) if (EFI_ERROR(err))
return err; return err;
*key = KEYPRESS(0, k.ScanCode, k.UnicodeChar); *key = KEYPRESS(0, k.ScanCode, k.UnicodeChar);
return 0; return EFI_SUCCESS;
} }
static EFI_STATUS change_mode(UINTN mode) { static EFI_STATUS change_mode(UINTN mode) {
@ -108,6 +154,8 @@ static EFI_STATUS mode_auto(UINTN *mode) {
EFI_STATUS err; EFI_STATUS err;
BOOLEAN keep = FALSE; BOOLEAN keep = FALSE;
assert(mode);
err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput); err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) { if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) {
Info = GraphicsOutput->Mode->Info; Info = GraphicsOutput->Mode->Info;
@ -156,6 +204,8 @@ static EFI_STATUS mode_auto(UINTN *mode) {
} }
EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how) { EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how) {
assert(mode);
if (how == CONSOLE_MODE_AUTO) if (how == CONSOLE_MODE_AUTO)
return mode_auto(mode); return mode_auto(mode);

View File

@ -16,5 +16,5 @@ enum console_mode_change_type {
CONSOLE_MODE_MAX, CONSOLE_MODE_MAX,
}; };
EFI_STATUS console_key_read(UINT64 *key, BOOLEAN wait); EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec);
EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how); EFI_STATUS console_set_mode(UINTN *mode, enum console_mode_change_type how);

View File

@ -42,6 +42,7 @@
*/ */
#include "crc32.h" #include "crc32.h"
#include "macro-fundamental.h"
static const UINT32 crc32_tab[] = { static const UINT32 crc32_tab[] = {
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
@ -111,6 +112,8 @@ UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len) {
const UINT8 *p = buf; const UINT8 *p = buf;
UINT32 crc = seed; UINT32 crc = seed;
assert(buf);
while (len > 0) { while (len > 0) {
crc = crc32_add_char(crc, *p++); crc = crc32_add_char(crc, *p++);
len--; len--;
@ -129,6 +132,8 @@ UINT32 crc32_exclude_offset(
const UINT8 *p = buf; const UINT8 *p = buf;
UINT32 crc = seed; UINT32 crc = seed;
assert(buf);
for (UINTN i = 0; i < len; i++) { for (UINTN i = 0; i < len; i++) {
UINT8 x = *p++; UINT8 x = *p++;

View File

@ -9,6 +9,8 @@
EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[static 37]) { EFI_STATUS disk_get_part_uuid(EFI_HANDLE *handle, CHAR16 uuid[static 37]) {
EFI_DEVICE_PATH *device_path; EFI_DEVICE_PATH *device_path;
assert(handle);
/* export the device path this image is started from */ /* export the device path this image is started from */
device_path = DevicePathFromHandle(handle); device_path = DevicePathFromHandle(handle);
if (device_path) { if (device_path) {

View File

@ -17,6 +17,8 @@ static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
handover_f handover; handover_f handover;
UINTN start = (UINTN)params->hdr.code32_start; UINTN start = (UINTN)params->hdr.code32_start;
assert(params);
#ifdef __x86_64__ #ifdef __x86_64__
asm volatile ("cli"); asm volatile ("cli");
start += 512; start += 512;
@ -35,6 +37,9 @@ EFI_STATUS linux_exec(EFI_HANDLE *image,
EFI_PHYSICAL_ADDRESS addr; EFI_PHYSICAL_ADDRESS addr;
EFI_STATUS err; EFI_STATUS err;
assert(image);
assert(cmdline);
image_params = (struct boot_params *) linux_addr; image_params = (struct boot_params *) linux_addr;
if (image_params->hdr.boot_flag != 0xAA55 || if (image_params->hdr.boot_flag != 0xAA55 ||

View File

@ -5,6 +5,7 @@
#include <efi.h> #include <efi.h>
#include <efilib.h> #include <efilib.h>
#include "macro-fundamental.h"
#include "measure.h" #include "measure.h"
#define EFI_TCG_GUID \ #define EFI_TCG_GUID \
@ -184,8 +185,10 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 p
EFI_PHYSICAL_ADDRESS event_log_last; EFI_PHYSICAL_ADDRESS event_log_last;
UINTN desc_len; UINTN desc_len;
desc_len = (StrLen(description) + 1) * sizeof(CHAR16); assert(tcg);
assert(description);
desc_len = StrSize(description);
tcg_event = AllocateZeroPool(desc_len + sizeof(TCG_PCR_EVENT)); tcg_event = AllocateZeroPool(desc_len + sizeof(TCG_PCR_EVENT));
if (!tcg_event) if (!tcg_event)
@ -215,14 +218,16 @@ static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32
EFI_TCG2_EVENT *tcg_event; EFI_TCG2_EVENT *tcg_event;
UINTN desc_len; UINTN desc_len;
desc_len = StrLen(description) * sizeof(CHAR16); assert(tcg);
assert(description);
tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1); desc_len = StrSize(description);
tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len);
if (!tcg_event) if (!tcg_event)
return EFI_OUT_OF_RESOURCES; return EFI_OUT_OF_RESOURCES;
tcg_event->Size = sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1; tcg_event->Size = sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len;
tcg_event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER); tcg_event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER);
tcg_event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; tcg_event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION;
tcg_event->Header.PCRIndex = pcrindex; tcg_event->Header.PCRIndex = pcrindex;
@ -302,6 +307,8 @@ EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UIN
EFI_TCG *tpm1; EFI_TCG *tpm1;
EFI_TCG2 *tpm2; EFI_TCG2 *tpm2;
assert(description);
tpm2 = tcg2_interface_check(); tpm2 = tcg2_interface_check();
if (tpm2) if (tpm2)
return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description); return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);

View File

@ -17,6 +17,7 @@ efi_headers = files('''
'''.split()) '''.split())
common_sources = ''' common_sources = '''
assert.c
disk.c disk.c
graphics.c graphics.c
measure.c measure.c
@ -219,12 +220,16 @@ if have_gnu_efi
compile_args += ['-Werror'] compile_args += ['-Werror']
endif endif
if get_option('buildtype') == 'debug' if get_option('buildtype') == 'debug'
compile_args += ['-ggdb', '-O0'] compile_args += ['-ggdb', '-O0', '-DEFI_DEBUG']
elif get_option('buildtype') == 'debugoptimized' elif get_option('buildtype') == 'debugoptimized'
compile_args += ['-ggdb', '-Og'] compile_args += ['-ggdb', '-Og', '-DEFI_DEBUG']
else else
compile_args += ['-O2'] compile_args += ['-O2']
endif endif
if get_option('b_ndebug') == 'true' or (
get_option('b_ndebug') == 'if-release' and ['plain', 'release'].contains(get_option('buildtype')))
compile_args += ['-DNDEBUG']
endif
efi_ldflags = ['-T', efi_lds, efi_ldflags = ['-T', efi_lds,
'-shared', '-shared',

View File

@ -66,6 +66,9 @@ EFI_STATUS pe_memory_locate_sections(CHAR8 *base, CHAR8 **sections, UINTN *addrs
struct PeHeader *pe; struct PeHeader *pe;
UINTN offset; UINTN offset;
assert(base);
assert(sections);
dos = (struct DosFileHeader *)base; dos = (struct DosFileHeader *)base;
if (CompareMem(dos->Magic, "MZ", 2) != 0) if (CompareMem(dos->Magic, "MZ", 2) != 0)
@ -118,6 +121,9 @@ EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections
EFI_STATUS err; EFI_STATUS err;
_cleanup_freepool_ CHAR8 *header = NULL; _cleanup_freepool_ CHAR8 *header = NULL;
assert(dir);
assert(path);
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL); err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err)) if (EFI_ERROR(err))
return err; return err;

View File

@ -22,6 +22,8 @@ static EFI_STATUS acquire_rng(UINTN size, VOID **ret) {
EFI_RNG_PROTOCOL *rng; EFI_RNG_PROTOCOL *rng;
EFI_STATUS err; EFI_STATUS err;
assert(ret);
/* Try to acquire the specified number of bytes from the UEFI RNG */ /* Try to acquire the specified number of bytes from the UEFI RNG */
err = LibLocateProtocol(EFI_RNG_GUID, (VOID**) &rng); err = LibLocateProtocol(EFI_RNG_GUID, (VOID**) &rng);
@ -35,10 +37,8 @@ static EFI_STATUS acquire_rng(UINTN size, VOID **ret) {
return log_oom(); return log_oom();
err = uefi_call_wrapper(rng->GetRNG, 3, rng, NULL, size, data); err = uefi_call_wrapper(rng->GetRNG, 3, rng, NULL, size, data);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Failed to acquire RNG data: %r\n", err); return log_error_status_stall(err, L"Failed to acquire RNG data: %r", err);
return err;
}
*ret = TAKE_PTR(data); *ret = TAKE_PTR(data);
return EFI_SUCCESS; return EFI_SUCCESS;
@ -65,6 +65,10 @@ static VOID hash_once(
struct sha256_ctx hash; struct sha256_ctx hash;
assert(old_seed);
assert(rng);
assert(system_token);
sha256_init_ctx(&hash); sha256_init_ctx(&hash);
sha256_process_bytes(old_seed, size, &hash); sha256_process_bytes(old_seed, size, &hash);
if (rng) if (rng)
@ -87,6 +91,11 @@ static EFI_STATUS hash_many(
_cleanup_freepool_ VOID *output = NULL; _cleanup_freepool_ VOID *output = NULL;
assert(old_seed);
assert(rng);
assert(system_token);
assert(ret);
/* Hashes the specified parameters in counter mode, generating n hash values, with the counter in the /* Hashes the specified parameters in counter mode, generating n hash values, with the counter in the
* range counter_startcounter_start+n-1. */ * range counter_startcounter_start+n-1. */
@ -117,6 +126,12 @@ static EFI_STATUS mangle_random_seed(
EFI_STATUS err; EFI_STATUS err;
UINTN n; UINTN n;
assert(old_seed);
assert(rng);
assert(system_token);
assert(ret_new_seed);
assert(ret_for_kernel);
/* This takes the old seed file contents, an (optional) random number acquired from the UEFI RNG, an /* This takes the old seed file contents, an (optional) random number acquired from the UEFI RNG, an
* (optional) system 'token' installed once by the OS installer in an EFI variable, and hashes them * (optional) system 'token' installed once by the OS installer in an EFI variable, and hashes them
* together in counter mode, generating a new seed (to replace the file on disk) and the seed for the * together in counter mode, generating a new seed (to replace the file on disk) and the seed for the
@ -146,17 +161,18 @@ static EFI_STATUS acquire_system_token(VOID **ret, UINTN *ret_size) {
EFI_STATUS err; EFI_STATUS err;
UINTN size; UINTN size;
assert(ret);
assert(ret_size);
err = efivar_get_raw(LOADER_GUID, L"LoaderSystemToken", &data, &size); err = efivar_get_raw(LOADER_GUID, L"LoaderSystemToken", &data, &size);
if (EFI_ERROR(err)) { if (EFI_ERROR(err)) {
if (err != EFI_NOT_FOUND) if (err != EFI_NOT_FOUND)
Print(L"Failed to read LoaderSystemToken EFI variable: %r", err); log_error_stall(L"Failed to read LoaderSystemToken EFI variable: %r", err);
return err; return err;
} }
if (size <= 0) { if (size <= 0)
Print(L"System token too short, ignoring."); return log_error_status_stall(EFI_NOT_FOUND, L"System token too short, ignoring.");
return EFI_NOT_FOUND;
}
*ret = TAKE_PTR(data); *ret = TAKE_PTR(data);
*ret_size = size; *ret_size = size;
@ -166,7 +182,7 @@ static EFI_STATUS acquire_system_token(VOID **ret, UINTN *ret_size) {
static VOID validate_sha256(void) { static VOID validate_sha256(void) {
#ifndef __OPTIMIZE__ #ifdef EFI_DEBUG
/* Let's validate our SHA256 implementation. We stole it from glibc, and converted it to UEFI /* Let's validate our SHA256 implementation. We stole it from glibc, and converted it to UEFI
* style. We better check whether it does the right stuff. We use the simpler test vectors from the * style. We better check whether it does the right stuff. We use the simpler test vectors from the
* SHA spec. Note that we strip this out in optimization builds. */ * SHA spec. Note that we strip this out in optimization builds. */
@ -208,14 +224,9 @@ static VOID validate_sha256(void) {
sha256_process_bytes(array[i].string, strlena((const CHAR8*) array[i].string), &hash); sha256_process_bytes(array[i].string, strlena((const CHAR8*) array[i].string), &hash);
sha256_finish_ctx(&hash, result); sha256_finish_ctx(&hash, result);
if (CompareMem(result, array[i].hash, HASH_VALUE_SIZE) != 0) { assert(CompareMem(result, array[i].hash, HASH_VALUE_SIZE) == 0);
Print(L"SHA256 failed validation.\n");
uefi_call_wrapper(BS->Stall, 1, 120 * 1000 * 1000);
return;
}
} }
Print(L"SHA256 validated\n");
#endif #endif
} }
@ -226,6 +237,8 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
_cleanup_freepool_ EFI_FILE_INFO *info = NULL; _cleanup_freepool_ EFI_FILE_INFO *info = NULL;
EFI_STATUS err; EFI_STATUS err;
assert(root_dir);
validate_sha256(); validate_sha256();
if (mode == RANDOM_SEED_OFF) if (mode == RANDOM_SEED_OFF)
@ -246,7 +259,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, (CHAR16*) L"\\loader\\random-seed", EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL); err = uefi_call_wrapper(root_dir->Open, 5, root_dir, &handle, (CHAR16*) L"\\loader\\random-seed", EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL);
if (EFI_ERROR(err)) { if (EFI_ERROR(err)) {
if (err != EFI_NOT_FOUND && err != EFI_WRITE_PROTECTED) if (err != EFI_NOT_FOUND && err != EFI_WRITE_PROTECTED)
Print(L"Failed to open random seed file: %r\n", err); log_error_stall(L"Failed to open random seed file: %r", err);
return err; return err;
} }
@ -255,15 +268,11 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
return log_oom(); return log_oom();
size = info->FileSize; size = info->FileSize;
if (size < RANDOM_MAX_SIZE_MIN) { if (size < RANDOM_MAX_SIZE_MIN)
Print(L"Random seed file is too short?\n"); return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too short.");
return EFI_INVALID_PARAMETER;
}
if (size > RANDOM_MAX_SIZE_MAX) { if (size > RANDOM_MAX_SIZE_MAX)
Print(L"Random seed file is too large?\n"); return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too large.");
return EFI_INVALID_PARAMETER;
}
seed = AllocatePool(size); seed = AllocatePool(size);
if (!seed) if (!seed)
@ -271,20 +280,14 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
rsize = size; rsize = size;
err = uefi_call_wrapper(handle->Read, 3, handle, &rsize, seed); err = uefi_call_wrapper(handle->Read, 3, handle, &rsize, seed);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Failed to read random seed file: %r\n", err); return log_error_status_stall(err, L"Failed to read random seed file: %r", err);
return err; if (rsize != size)
} return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short read on random seed file.");
if (rsize != size) {
Print(L"Short read on random seed file\n");
return EFI_PROTOCOL_ERROR;
}
err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0); err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Failed to seek to beginning of random seed file: %r\n", err); return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
return err;
}
/* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good /* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
* idea to use it because it helps us for cases where users mistakenly include a random seed in * idea to use it because it helps us for cases where users mistakenly include a random seed in
@ -299,27 +302,19 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
/* Update the random seed on disk before we use it */ /* Update the random seed on disk before we use it */
wsize = size; wsize = size;
err = uefi_call_wrapper(handle->Write, 3, handle, &wsize, new_seed); err = uefi_call_wrapper(handle->Write, 3, handle, &wsize, new_seed);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Failed to write random seed file: %r\n", err); return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
return err; if (wsize != size)
} return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
if (wsize != size) {
Print(L"Short write on random seed file\n");
return EFI_PROTOCOL_ERROR;
}
err = uefi_call_wrapper(handle->Flush, 1, handle); err = uefi_call_wrapper(handle->Flush, 1, handle);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Failed to flush random seed file: %r\n"); return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
return err;
}
/* We are good to go */ /* We are good to go */
err = efivar_set_raw(LOADER_GUID, L"LoaderRandomSeed", for_kernel, size, 0); err = efivar_set_raw(LOADER_GUID, L"LoaderRandomSeed", for_kernel, size, 0);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Failed to write random seed to EFI variable: %r\n", err); return log_error_status_stall(err, L"Failed to write random seed to EFI variable: %r", err);
return err;
}
return EFI_SUCCESS; return EFI_SUCCESS;
} }

View File

@ -23,6 +23,7 @@
/* Written by Ulrich Drepper <drepper@redhat.com>, 2007. */ /* Written by Ulrich Drepper <drepper@redhat.com>, 2007. */
#include "macro-fundamental.h"
#include "sha256.h" #include "sha256.h"
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
@ -73,6 +74,8 @@ static void sha256_process_block(const void *, UINTN, struct sha256_ctx *);
/* Initialize structure containing state of computation. /* Initialize structure containing state of computation.
(FIPS 180-2:5.3.2) */ (FIPS 180-2:5.3.2) */
void sha256_init_ctx(struct sha256_ctx *ctx) { void sha256_init_ctx(struct sha256_ctx *ctx) {
assert(ctx);
ctx->H[0] = 0x6a09e667; ctx->H[0] = 0x6a09e667;
ctx->H[1] = 0xbb67ae85; ctx->H[1] = 0xbb67ae85;
ctx->H[2] = 0x3c6ef372; ctx->H[2] = 0x3c6ef372;
@ -96,6 +99,9 @@ void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
UINT32 bytes = ctx->buflen; UINT32 bytes = ctx->buflen;
UINTN pad; UINTN pad;
assert(ctx);
assert(resbuf);
/* Now count remaining bytes. */ /* Now count remaining bytes. */
ctx->total64 += bytes; ctx->total64 += bytes;
@ -118,6 +124,9 @@ void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
} }
void sha256_process_bytes(const void *buffer, UINTN len, struct sha256_ctx *ctx) { void sha256_process_bytes(const void *buffer, UINTN len, struct sha256_ctx *ctx) {
assert(buffer);
assert(ctx);
/* When we already have some bits in our internal buffer concatenate /* When we already have some bits in our internal buffer concatenate
both inputs first. */ both inputs first. */
@ -191,6 +200,10 @@ void sha256_process_bytes(const void *buffer, UINTN len, struct sha256_ctx *ctx)
static void sha256_process_block(const void *buffer, UINTN len, struct sha256_ctx *ctx) { static void sha256_process_block(const void *buffer, UINTN len, struct sha256_ctx *ctx) {
const UINT32 *words = buffer; const UINT32 *words = buffer;
UINTN nwords = len / sizeof (UINT32); UINTN nwords = len / sizeof (UINT32);
assert(buffer);
assert(ctx);
UINT32 a = ctx->H[0]; UINT32 a = ctx->H[0];
UINT32 b = ctx->H[1]; UINT32 b = ctx->H[1];
UINT32 c = ctx->H[2]; UINT32 c = ctx->H[2];

View File

@ -111,6 +111,10 @@ static EFIAPI EFI_STATUS security2_policy_authentication (const EFI_SECURITY2_PR
VOID *file_buffer, UINTN file_size, BOOLEAN boot_policy) { VOID *file_buffer, UINTN file_size, BOOLEAN boot_policy) {
EFI_STATUS status; EFI_STATUS status;
assert(this);
assert(device_path);
assert(file_buffer);
/* Chain original security policy */ /* Chain original security policy */
status = uefi_call_wrapper(es2fa, 5, this, device_path, file_buffer, file_size, boot_policy); status = uefi_call_wrapper(es2fa, 5, this, device_path, file_buffer, file_size, boot_policy);
@ -143,6 +147,9 @@ static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROT
_cleanup_freepool_ CHAR8 *file_buffer = NULL; _cleanup_freepool_ CHAR8 *file_buffer = NULL;
UINTN file_size; UINTN file_size;
assert(this);
assert(device_path_const);
if (!device_path_const) if (!device_path_const)
return EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;

View File

@ -44,6 +44,11 @@ static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_
struct bmp_map *map; struct bmp_map *map;
UINTN row_size; UINTN row_size;
assert(bmp);
assert(ret_dib);
assert(ret_map);
assert(pixmap);
if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib)) if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib))
return EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;
@ -128,6 +133,8 @@ static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_
static VOID pixel_blend(UINT32 *dst, const UINT32 source) { static VOID pixel_blend(UINT32 *dst, const UINT32 source) {
UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g; UINT32 alpha, src, src_rb, src_g, dst_rb, dst_g, rb, g;
assert(dst);
alpha = (source & 0xff); alpha = (source & 0xff);
/* convert src from RGBA to XRGB */ /* convert src from RGBA to XRGB */
@ -152,6 +159,11 @@ static EFI_STATUS bmp_to_blt(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf,
UINT8 *pixmap) { UINT8 *pixmap) {
UINT8 *in; UINT8 *in;
assert(buf);
assert(dib);
assert(map);
assert(pixmap);
/* transform and copy pixels */ /* transform and copy pixels */
in = pixmap; in = pixmap;
for (UINTN y = 0; y < dib->y; y++) { for (UINTN y = 0; y < dib->y; y++) {
@ -247,6 +259,8 @@ EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_
UINTN y_pos = 0; UINTN y_pos = 0;
EFI_STATUS err; EFI_STATUS err;
assert(content);
if (!background) { if (!background) {
if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) { if (StriCmp(L"Apple", ST->FirmwareVendor) == 0) {
pixel.Red = 0xc0; pixel.Red = 0xc0;

View File

@ -36,18 +36,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image, err = uefi_call_wrapper(BS->OpenProtocol, 6, image, &LoadedImageProtocol, (VOID **)&loaded_image,
image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Error getting a LoadedImageProtocol handle: %r ", err); return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
}
err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, offs, szs); err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, offs, szs);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Unable to locate embedded .linux section: %r ", err); return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
}
if (szs[0] > 0) if (szs[0] > 0)
cmdline = (CHAR8 *)(loaded_image->ImageBase) + addrs[0]; cmdline = (CHAR8 *)(loaded_image->ImageBase) + addrs[0];
@ -72,10 +66,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = tpm_log_event(SD_TPM_PCR, err = tpm_log_event(SD_TPM_PCR,
(EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions, (EFI_PHYSICAL_ADDRESS) (UINTN) loaded_image->LoadOptions,
loaded_image->LoadOptionsSize, loaded_image->LoadOptions); loaded_image->LoadOptionsSize, loaded_image->LoadOptions);
if (EFI_ERROR(err)) { if (EFI_ERROR(err))
Print(L"Unable to add image options measurement: %r", err); log_error_stall(L"Unable to add image options measurement: %r", err);
uefi_call_wrapper(BS->Stall, 1, 200 * 1000);
}
#endif #endif
} }
@ -126,7 +118,5 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
(UINTN)loaded_image->ImageBase + addrs[2], szs[2]); (UINTN)loaded_image->ImageBase + addrs[2], szs[2]);
graphics_mode(FALSE); graphics_mode(FALSE);
Print(L"Execution of embedded linux image failed: %r\n", err); return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return err;
} }

View File

@ -67,13 +67,17 @@ UINT64 time_usec(VOID) {
} }
EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) { EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
assert(b);
if (!v) if (!v)
return EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;
if (strcmpa(v, (CHAR8 *)"1") == 0 || if (strcmpa(v, (CHAR8 *)"1") == 0 ||
strcmpa(v, (CHAR8 *)"yes") == 0 || strcmpa(v, (CHAR8 *)"yes") == 0 ||
strcmpa(v, (CHAR8 *)"y") == 0 || strcmpa(v, (CHAR8 *)"y") == 0 ||
strcmpa(v, (CHAR8 *)"true") == 0) { strcmpa(v, (CHAR8 *)"true") == 0 ||
strcmpa(v, (CHAR8 *)"t") == 0 ||
strcmpa(v, (CHAR8 *)"on") == 0) {
*b = TRUE; *b = TRUE;
return EFI_SUCCESS; return EFI_SUCCESS;
} }
@ -81,7 +85,9 @@ EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
if (strcmpa(v, (CHAR8 *)"0") == 0 || if (strcmpa(v, (CHAR8 *)"0") == 0 ||
strcmpa(v, (CHAR8 *)"no") == 0 || strcmpa(v, (CHAR8 *)"no") == 0 ||
strcmpa(v, (CHAR8 *)"n") == 0 || strcmpa(v, (CHAR8 *)"n") == 0 ||
strcmpa(v, (CHAR8 *)"false") == 0) { strcmpa(v, (CHAR8 *)"false") == 0 ||
strcmpa(v, (CHAR8 *)"f") == 0 ||
strcmpa(v, (CHAR8 *)"off") == 0) {
*b = FALSE; *b = FALSE;
return EFI_SUCCESS; return EFI_SUCCESS;
} }
@ -90,24 +96,37 @@ EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) {
} }
EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, UINT32 flags) { EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const VOID *buf, UINTN size, UINT32 flags) {
assert(vendor);
assert(name);
assert(buf || size == 0);
flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; flags |= EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf); return uefi_call_wrapper(RT->SetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, flags, size, (VOID*) buf);
} }
EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags) { EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags) {
return efivar_set_raw(vendor, name, value, value ? (StrLen(value) + 1) * sizeof(CHAR16) : 0, flags); assert(vendor);
assert(name);
return efivar_set_raw(vendor, name, value, value ? StrSize(value) : 0, flags);
} }
EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags) { EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags) {
CHAR16 str[32]; CHAR16 str[32];
SPrint(str, 32, L"%u", i); assert(vendor);
assert(name);
SPrint(str, ELEMENTSOF(str), L"%u", i);
return efivar_set(vendor, name, str, flags); return efivar_set(vendor, name, str, flags);
} }
EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 value, UINT32 flags) { EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT32 value, UINT32 flags) {
UINT8 buf[4]; UINT8 buf[4];
assert(vendor);
assert(name);
buf[0] = (UINT8)(value >> 0U & 0xFF); buf[0] = (UINT8)(value >> 0U & 0xFF);
buf[1] = (UINT8)(value >> 8U & 0xFF); buf[1] = (UINT8)(value >> 8U & 0xFF);
buf[2] = (UINT8)(value >> 16U & 0xFF); buf[2] = (UINT8)(value >> 16U & 0xFF);
@ -119,6 +138,9 @@ EFI_STATUS efivar_set_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 value, UINT32 flags) { EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT64 value, UINT32 flags) {
UINT8 buf[8]; UINT8 buf[8];
assert(vendor);
assert(name);
buf[0] = (UINT8)(value >> 0U & 0xFF); buf[0] = (UINT8)(value >> 0U & 0xFF);
buf[1] = (UINT8)(value >> 8U & 0xFF); buf[1] = (UINT8)(value >> 8U & 0xFF);
buf[2] = (UINT8)(value >> 16U & 0xFF); buf[2] = (UINT8)(value >> 16U & 0xFF);
@ -132,35 +154,38 @@ EFI_STATUS efivar_set_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
} }
EFI_STATUS efivar_get(const EFI_GUID *vendor, const CHAR16 *name, CHAR16 **value) { EFI_STATUS efivar_get(const EFI_GUID *vendor, const CHAR16 *name, CHAR16 **value) {
_cleanup_freepool_ CHAR8 *buf = NULL; _cleanup_freepool_ CHAR16 *buf = NULL;
EFI_STATUS err; EFI_STATUS err;
CHAR16 *val; CHAR16 *val;
UINTN size; UINTN size;
err = efivar_get_raw(vendor, name, &buf, &size); assert(vendor);
assert(name);
err = efivar_get_raw(vendor, name, (CHAR8**)&buf, &size);
if (EFI_ERROR(err)) if (EFI_ERROR(err))
return err; return err;
/* Make sure there are no incomplete characters in the buffer */ /* Make sure there are no incomplete characters in the buffer */
if ((size % 2) != 0) if ((size % sizeof(CHAR16)) != 0)
return EFI_INVALID_PARAMETER; return EFI_INVALID_PARAMETER;
if (!value) if (!value)
return EFI_SUCCESS; return EFI_SUCCESS;
/* Return buffer directly if it happens to be NUL terminated already */ /* Return buffer directly if it happens to be NUL terminated already */
if (size >= 2 && buf[size-2] == 0 && buf[size-1] == 0) { if (size >= sizeof(CHAR16) && buf[size/sizeof(CHAR16)] == 0) {
*value = (CHAR16*) TAKE_PTR(buf); *value = TAKE_PTR(buf);
return EFI_SUCCESS; return EFI_SUCCESS;
} }
/* Make sure a terminating NUL is available at the end */ /* Make sure a terminating NUL is available at the end */
val = AllocatePool(size + 2); val = AllocatePool(size + sizeof(CHAR16));
if (!val) if (!val)
return EFI_OUT_OF_RESOURCES; return EFI_OUT_OF_RESOURCES;
CopyMem(val, buf, size); CopyMem(val, buf, size);
val[size/2] = 0; /* NUL terminate */ val[size / sizeof(CHAR16)] = 0; /* NUL terminate */
*value = val; *value = val;
return EFI_SUCCESS; return EFI_SUCCESS;
@ -170,8 +195,12 @@ EFI_STATUS efivar_get_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UI
_cleanup_freepool_ CHAR16 *val = NULL; _cleanup_freepool_ CHAR16 *val = NULL;
EFI_STATUS err; EFI_STATUS err;
assert(vendor);
assert(name);
assert(i);
err = efivar_get(vendor, name, &val); err = efivar_get(vendor, name, &val);
if (!EFI_ERROR(err) && i) if (!EFI_ERROR(err))
*i = Atoi(val); *i = Atoi(val);
return err; return err;
@ -182,6 +211,9 @@ EFI_STATUS efivar_get_uint32_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
UINTN size; UINTN size;
EFI_STATUS err; EFI_STATUS err;
assert(vendor);
assert(name);
err = efivar_get_raw(vendor, name, &buf, &size); err = efivar_get_raw(vendor, name, &buf, &size);
if (!EFI_ERROR(err) && ret) { if (!EFI_ERROR(err) && ret) {
if (size != sizeof(UINT32)) if (size != sizeof(UINT32))
@ -199,6 +231,9 @@ EFI_STATUS efivar_get_uint64_le(const EFI_GUID *vendor, const CHAR16 *name, UINT
UINTN size; UINTN size;
EFI_STATUS err; EFI_STATUS err;
assert(vendor);
assert(name);
err = efivar_get_raw(vendor, name, &buf, &size); err = efivar_get_raw(vendor, name, &buf, &size);
if (!EFI_ERROR(err) && ret) { if (!EFI_ERROR(err) && ret) {
if (size != sizeof(UINT64)) if (size != sizeof(UINT64))
@ -217,6 +252,9 @@ EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **bu
UINTN l; UINTN l;
EFI_STATUS err; EFI_STATUS err;
assert(vendor);
assert(name);
l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE; l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE;
buf = AllocatePool(l); buf = AllocatePool(l);
if (!buf) if (!buf)
@ -240,6 +278,10 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const CHAR16 *name, BOO
UINTN size; UINTN size;
EFI_STATUS err; EFI_STATUS err;
assert(vendor);
assert(name);
assert(ret);
err = efivar_get_raw(vendor, name, &b, &size); err = efivar_get_raw(vendor, name, &b, &size);
if (!EFI_ERROR(err)) if (!EFI_ERROR(err))
*ret = *b > 0; *ret = *b > 0;
@ -250,12 +292,15 @@ EFI_STATUS efivar_get_boolean_u8(const EFI_GUID *vendor, const CHAR16 *name, BOO
VOID efivar_set_time_usec(const EFI_GUID *vendor, const CHAR16 *name, UINT64 usec) { VOID efivar_set_time_usec(const EFI_GUID *vendor, const CHAR16 *name, UINT64 usec) {
CHAR16 str[32]; CHAR16 str[32];
assert(vendor);
assert(name);
if (usec == 0) if (usec == 0)
usec = time_usec(); usec = time_usec();
if (usec == 0) if (usec == 0)
return; return;
SPrint(str, 32, L"%ld", usec); SPrint(str, ELEMENTSOF(str), L"%ld", usec);
efivar_set(vendor, name, str, 0); efivar_set(vendor, name, str, 0);
} }
@ -263,6 +308,9 @@ static INTN utf8_to_16(const CHAR8 *stra, CHAR16 *c) {
CHAR16 unichar; CHAR16 unichar;
UINTN len; UINTN len;
assert(stra);
assert(c);
if (!(stra[0] & 0x80)) if (!(stra[0] & 0x80))
len = 1; len = 1;
else if ((stra[0] & 0xe0) == 0xc0) else if ((stra[0] & 0xe0) == 0xc0)
@ -316,6 +364,8 @@ CHAR16 *stra_to_str(const CHAR8 *stra) {
UINTN i; UINTN i;
CHAR16 *str; CHAR16 *str;
assert(stra);
len = strlena(stra); len = strlena(stra);
str = AllocatePool((len + 1) * sizeof(CHAR16)); str = AllocatePool((len + 1) * sizeof(CHAR16));
@ -344,6 +394,8 @@ CHAR16 *stra_to_path(const CHAR8 *stra) {
UINTN len; UINTN len;
UINTN i; UINTN i;
assert(stra);
len = strlena(stra); len = strlena(stra);
str = AllocatePool((len + 2) * sizeof(CHAR16)); str = AllocatePool((len + 2) * sizeof(CHAR16));
@ -376,6 +428,7 @@ CHAR16 *stra_to_path(const CHAR8 *stra) {
} }
CHAR8 *strchra(const CHAR8 *s, CHAR8 c) { CHAR8 *strchra(const CHAR8 *s, CHAR8 c) {
assert(s);
do { do {
if (*s == c) if (*s == c)
return (CHAR8*) s; return (CHAR8*) s;
@ -388,6 +441,9 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s
_cleanup_freepool_ CHAR8 *buf = NULL; _cleanup_freepool_ CHAR8 *buf = NULL;
EFI_STATUS err; EFI_STATUS err;
assert(name);
assert(ret);
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL); err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*) name, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err)) if (EFI_ERROR(err))
return err; return err;
@ -425,8 +481,23 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s
return err; return err;
} }
VOID log_error_stall(const CHAR16 *fmt, ...) {
va_list args;
assert(fmt);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
Print(L"\n");
va_start(args, fmt);
VPrint(fmt, args);
va_end(args);
Print(L"\n");
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
}
EFI_STATUS log_oom(void) { EFI_STATUS log_oom(void) {
Print(L"Out of memory."); log_error_stall(L"Out of memory.");
(void) uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return EFI_OUT_OF_RESOURCES; return EFI_OUT_OF_RESOURCES;
} }

View File

@ -74,4 +74,13 @@ static inline void FileHandleClosep(EFI_FILE_HANDLE *handle) {
#define UINT64_MAX ((UINT64) -1) #define UINT64_MAX ((UINT64) -1)
#endif #endif
VOID log_error_stall(const CHAR16 *fmt, ...);
EFI_STATUS log_oom(void); EFI_STATUS log_oom(void);
/* This works just like log_error_errno() from userspace, but requires you
* to provide err a second time if you want to use %r in the message! */
#define log_error_status_stall(err, fmt, ...) \
({ \
log_error_stall(fmt, ##__VA_ARGS__); \
err; \
})

View File

@ -1541,7 +1541,7 @@ Manager* manager_free(Manager *m) {
static void manager_enumerate_perpetual(Manager *m) { static void manager_enumerate_perpetual(Manager *m) {
assert(m); assert(m);
if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL) if (FLAGS_SET(m->test_run_flags, MANAGER_TEST_RUN_MINIMAL))
return; return;
/* Let's ask every type to load all units from disk/kernel that it might know */ /* Let's ask every type to load all units from disk/kernel that it might know */
@ -1559,7 +1559,7 @@ static void manager_enumerate_perpetual(Manager *m) {
static void manager_enumerate(Manager *m) { static void manager_enumerate(Manager *m) {
assert(m); assert(m);
if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL) if (FLAGS_SET(m->test_run_flags, MANAGER_TEST_RUN_MINIMAL))
return; return;
/* Let's ask every type to load all units from disk/kernel that it might know */ /* Let's ask every type to load all units from disk/kernel that it might know */

View File

@ -128,11 +128,12 @@ typedef enum WatchdogType {
#include "unit-name.h" #include "unit-name.h"
typedef enum ManagerTestRunFlags { typedef enum ManagerTestRunFlags {
MANAGER_TEST_NORMAL = 0, /* run normally */ MANAGER_TEST_NORMAL = 0, /* run normally */
MANAGER_TEST_RUN_MINIMAL = 1 << 0, /* create basic data structures */ MANAGER_TEST_RUN_MINIMAL = 1 << 0, /* create basic data structures */
MANAGER_TEST_RUN_BASIC = 1 << 1, /* interact with the environment */ MANAGER_TEST_RUN_BASIC = 1 << 1, /* interact with the environment */
MANAGER_TEST_RUN_ENV_GENERATORS = 1 << 2, /* also run env generators */ MANAGER_TEST_RUN_ENV_GENERATORS = 1 << 2, /* also run env generators */
MANAGER_TEST_RUN_GENERATORS = 1 << 3, /* also run unit generators */ MANAGER_TEST_RUN_GENERATORS = 1 << 3, /* also run unit generators */
MANAGER_TEST_RUN_IGNORE_DEPENDENCIES = 1 << 4, /* run while ignoring dependencies */
MANAGER_TEST_FULL = MANAGER_TEST_RUN_BASIC | MANAGER_TEST_RUN_ENV_GENERATORS | MANAGER_TEST_RUN_GENERATORS, MANAGER_TEST_FULL = MANAGER_TEST_RUN_BASIC | MANAGER_TEST_RUN_ENV_GENERATORS | MANAGER_TEST_RUN_GENERATORS,
} ManagerTestRunFlags; } ManagerTestRunFlags;

View File

@ -3053,6 +3053,9 @@ int unit_add_dependency(
return 0; return 0;
} }
if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
return 0;
/* Note that ordering a device unit after a unit is permitted since it allows to start its job /* Note that ordering a device unit after a unit is permitted since it allows to start its job
* running timeout at a specific time. */ * running timeout at a specific time. */
if (FLAGS_SET(a, UNIT_ATOM_BEFORE) && other->type == UNIT_DEVICE) { if (FLAGS_SET(a, UNIT_ATOM_BEFORE) && other->type == UNIT_DEVICE) {
@ -3176,6 +3179,9 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, boo
if (r < 0) if (r < 0)
return r; return r;
if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
return 0;
r = manager_load_unit(u->manager, name, NULL, NULL, &other); r = manager_load_unit(u->manager, name, NULL, NULL, &other);
if (r < 0) if (r < 0)
return r; return r;
@ -3195,6 +3201,9 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency
if (r < 0) if (r < 0)
return r; return r;
if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
return 0;
r = manager_load_unit(u->manager, name, NULL, NULL, &other); r = manager_load_unit(u->manager, name, NULL, NULL, &other);
if (r < 0) if (r < 0)
return r; return r;
@ -3312,6 +3321,9 @@ int unit_set_default_slice(Unit *u) {
assert(u); assert(u);
if (u->manager && FLAGS_SET(u->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
return 0;
if (UNIT_GET_SLICE(u)) if (UNIT_GET_SLICE(u))
return 0; return 0;

View File

@ -14,6 +14,22 @@
#define _used_ __attribute__((__used__)) #define _used_ __attribute__((__used__))
#define _unused_ __attribute__((__unused__)) #define _unused_ __attribute__((__unused__))
#define _cleanup_(x) __attribute__((__cleanup__(x))) #define _cleanup_(x) __attribute__((__cleanup__(x)))
#define _likely_(x) (__builtin_expect(!!(x), 1))
#define _unlikely_(x) (__builtin_expect(!!(x), 0))
#if __GNUC__ >= 7
#define _fallthrough_ __attribute__((__fallthrough__))
#else
#define _fallthrough_
#endif
/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
* compiler versions */
#ifndef _noreturn_
#if __STDC_VERSION__ >= 201112L
#define _noreturn_ _Noreturn
#else
#define _noreturn_ __attribute__((__noreturn__))
#endif
#endif
#define XSTRINGIFY(x) #x #define XSTRINGIFY(x) #x
#define STRINGIFY(x) XSTRINGIFY(x) #define STRINGIFY(x) XSTRINGIFY(x)
@ -34,7 +50,14 @@
#define CONCATENATE(x, y) XCONCATENATE(x, y) #define CONCATENATE(x, y) XCONCATENATE(x, y)
#ifdef SD_BOOT #ifdef SD_BOOT
#define assert(expr) do {} while (false) #ifdef NDEBUG
#define assert(expr)
#define assert_not_reached() __builtin_unreachable()
#else
void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_;
#define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
#define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
#endif
#endif #endif
#if defined(static_assert) #if defined(static_assert)
@ -233,3 +256,10 @@
(ptr) = NULL; \ (ptr) = NULL; \
_ptr_; \ _ptr_; \
}) })
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
* Contrary to strlen(), this is a constant expression.
* @x: a string literal.
*/
#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))

View File

@ -29,7 +29,7 @@ static int bare_udp_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *
log_netdev_info(netdev, "BareUDP netdev exists, using existing without changing its parameters."); log_netdev_info(netdev, "BareUDP netdev exists, using existing without changing its parameters.");
else if (r < 0) { else if (r < 0) {
log_netdev_warning_errno(netdev, r, "BareUDP netdev could not be created: %m"); log_netdev_warning_errno(netdev, r, "BareUDP netdev could not be created: %m");
netdev_drop(netdev); netdev_enter_failed(netdev);
return 1; return 1;
} }

View File

@ -108,7 +108,7 @@ static int fou_tunnel_create_handler(sd_netlink *rtnl, sd_netlink_message *m, Ne
log_netdev_info(netdev, "netdev exists, using existing without changing its parameters"); log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
else if (r < 0) { else if (r < 0) {
log_netdev_warning_errno(netdev, r, "netdev could not be created: %m"); log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
netdev_drop(netdev); netdev_enter_failed(netdev);
return 1; return 1;
} }

View File

@ -37,7 +37,7 @@ static int geneve_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m,
log_netdev_info(netdev, "Geneve netdev exists, using existing without changing its parameters"); log_netdev_info(netdev, "Geneve netdev exists, using existing without changing its parameters");
else if (r < 0) { else if (r < 0) {
log_netdev_warning_errno(netdev, r, "Geneve netdev could not be created: %m"); log_netdev_warning_errno(netdev, r, "Geneve netdev could not be created: %m");
netdev_drop(netdev); netdev_enter_failed(netdev);
return 1; return 1;
} }

View File

@ -355,7 +355,7 @@ static int l2tp_create_tunnel_handler(sd_netlink *rtnl, sd_netlink_message *m, N
log_netdev_info(netdev, "netdev exists, using existing without changing its parameters"); log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
else if (r < 0) { else if (r < 0) {
log_netdev_warning_errno(netdev, r, "netdev could not be created: %m"); log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
netdev_drop(netdev); netdev_enter_failed(netdev);
return 1; return 1;
} }

View File

@ -317,7 +317,7 @@ static int macsec_receive_association_handler(sd_netlink *rtnl, sd_netlink_messa
else if (r < 0) { else if (r < 0) {
log_netdev_warning_errno(netdev, r, log_netdev_warning_errno(netdev, r,
"Failed to add receive secure association: %m"); "Failed to add receive secure association: %m");
netdev_drop(netdev); netdev_enter_failed(netdev);
return 1; return 1;
} }
@ -375,7 +375,7 @@ static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *
else if (r < 0) { else if (r < 0) {
log_netdev_warning_errno(netdev, r, log_netdev_warning_errno(netdev, r,
"Failed to add receive secure channel: %m"); "Failed to add receive secure channel: %m");
netdev_drop(netdev); netdev_enter_failed(netdev);
return 1; return 1;
} }
@ -387,7 +387,7 @@ static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message *
if (r < 0) { if (r < 0) {
log_netdev_warning_errno(netdev, r, log_netdev_warning_errno(netdev, r,
"Failed to configure receive security association: %m"); "Failed to configure receive security association: %m");
netdev_drop(netdev); netdev_enter_failed(netdev);
return 1; return 1;
} }
} }
@ -441,7 +441,7 @@ static int macsec_transmit_association_handler(sd_netlink *rtnl, sd_netlink_mess
else if (r < 0) { else if (r < 0) {
log_netdev_warning_errno(netdev, r, log_netdev_warning_errno(netdev, r,
"Failed to add transmit secure association: %m"); "Failed to add transmit secure association: %m");
netdev_drop(netdev); netdev_enter_failed(netdev);
return 1; return 1;
} }

View File

@ -167,6 +167,54 @@ bool netdev_is_managed(NetDev *netdev) {
return hashmap_get(netdev->manager->netdevs, netdev->ifname) == netdev; return hashmap_get(netdev->manager->netdevs, netdev->ifname) == netdev;
} }
static bool netdev_is_stacked_and_independent(NetDev *netdev) {
assert(netdev);
if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
return false;
switch (netdev->kind) {
case NETDEV_KIND_ERSPAN:
return ERSPAN(netdev)->independent;
case NETDEV_KIND_GRE:
return GRE(netdev)->independent;
case NETDEV_KIND_GRETAP:
return GRETAP(netdev)->independent;
case NETDEV_KIND_IP6GRE:
return IP6GRE(netdev)->independent;
case NETDEV_KIND_IP6GRETAP:
return IP6GRETAP(netdev)->independent;
case NETDEV_KIND_IP6TNL:
return IP6TNL(netdev)->independent;
case NETDEV_KIND_IPIP:
return IPIP(netdev)->independent;
case NETDEV_KIND_SIT:
return SIT(netdev)->independent;
case NETDEV_KIND_VTI:
return VTI(netdev)->independent;
case NETDEV_KIND_VTI6:
return VTI6(netdev)->independent;
case NETDEV_KIND_VXLAN:
return VXLAN(netdev)->independent;
case NETDEV_KIND_XFRM:
return XFRM(netdev)->independent;
default:
return false;
}
}
static bool netdev_is_stacked(NetDev *netdev) {
assert(netdev);
if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
return false;
if (netdev_is_stacked_and_independent(netdev))
return false;
return true;
}
static void netdev_detach_from_manager(NetDev *netdev) { static void netdev_detach_from_manager(NetDev *netdev) {
if (netdev->ifname && netdev->manager) if (netdev->ifname && netdev->manager)
hashmap_remove(netdev->manager->netdevs, netdev->ifname); hashmap_remove(netdev->manager->netdevs, netdev->ifname);
@ -202,9 +250,19 @@ static NetDev *netdev_free(NetDev *netdev) {
DEFINE_TRIVIAL_REF_UNREF_FUNC(NetDev, netdev, netdev_free); DEFINE_TRIVIAL_REF_UNREF_FUNC(NetDev, netdev, netdev_free);
void netdev_drop(NetDev *netdev) { void netdev_drop(NetDev *netdev) {
if (!netdev || netdev->state == NETDEV_STATE_LINGER) if (!netdev)
return; return;
if (netdev_is_stacked(netdev)) {
/* The netdev may be removed due to the underlying device removal, and the device may
* be re-added later. */
netdev->state = NETDEV_STATE_LOADING;
netdev->ifindex = 0;
log_netdev_debug(netdev, "netdev removed");
return;
}
netdev->state = NETDEV_STATE_LINGER; netdev->state = NETDEV_STATE_LINGER;
log_netdev_debug(netdev, "netdev removed"); log_netdev_debug(netdev, "netdev removed");
@ -232,9 +290,8 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret) {
return 0; return 0;
} }
static int netdev_enter_failed(NetDev *netdev) { void netdev_enter_failed(NetDev *netdev) {
netdev->state = NETDEV_STATE_FAILED; netdev->state = NETDEV_STATE_FAILED;
return 0;
} }
static int netdev_enter_ready(NetDev *netdev) { static int netdev_enter_ready(NetDev *netdev) {
@ -266,7 +323,7 @@ static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev
log_netdev_info(netdev, "netdev exists, using existing without changing its parameters"); log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
else if (r < 0) { else if (r < 0) {
log_netdev_warning_errno(netdev, r, "netdev could not be created: %m"); log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
netdev_drop(netdev); netdev_enter_failed(netdev);
return 1; return 1;
} }
@ -560,12 +617,12 @@ static bool netdev_is_ready_to_create(NetDev *netdev, Link *link) {
return true; return true;
} }
int request_process_create_stacked_netdev(Request *req) { int request_process_stacked_netdev(Request *req) {
int r; int r;
assert(req); assert(req);
assert(req->link); assert(req->link);
assert(req->type == REQUEST_TYPE_CREATE_STACKED_NETDEV); assert(req->type == REQUEST_TYPE_STACKED_NETDEV);
assert(req->netdev); assert(req->netdev);
assert(req->netlink_handler); assert(req->netlink_handler);
@ -632,46 +689,42 @@ static int link_create_stacked_netdev_after_configured_handler(sd_netlink *rtnl,
return 0; return 0;
} }
int link_request_to_crate_stacked_netdev(Link *link, NetDev *netdev) { int link_request_stacked_netdev(Link *link, NetDev *netdev) {
NetDevCreateType create_type;
int r; int r;
assert(link); assert(link);
assert(netdev); assert(netdev);
create_type = netdev_get_create_type(netdev); if (!netdev_is_stacked(netdev))
if (!IN_SET(create_type, NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED))
return -EINVAL; return -EINVAL;
if (netdev->state != NETDEV_STATE_LOADING || netdev->ifindex > 0) if (!IN_SET(netdev->state, NETDEV_STATE_LOADING, NETDEV_STATE_FAILED) || netdev->ifindex > 0)
/* Already created (or removed?) */ return 0; /* Already created. */
return 0;
if (create_type == NETDEV_CREATE_STACKED) { if (netdev_get_create_type(netdev) == NETDEV_CREATE_STACKED) {
link->stacked_netdevs_created = false; link->stacked_netdevs_created = false;
r = link_queue_request(link, REQUEST_TYPE_CREATE_STACKED_NETDEV, netdev, false, r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false,
&link->create_stacked_netdev_messages, &link->create_stacked_netdev_messages,
link_create_stacked_netdev_handler, link_create_stacked_netdev_handler,
NULL); NULL);
} else { } else {
link->stacked_netdevs_after_configured_created = false; link->stacked_netdevs_after_configured_created = false;
r = link_queue_request(link, REQUEST_TYPE_CREATE_STACKED_NETDEV, netdev, false, r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false,
&link->create_stacked_netdev_after_configured_messages, &link->create_stacked_netdev_after_configured_messages,
link_create_stacked_netdev_after_configured_handler, link_create_stacked_netdev_after_configured_handler,
NULL); NULL);
} }
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Failed to request to create stacked netdev '%s': %m", return log_link_error_errno(link, r, "Failed to request stacked netdev '%s': %m",
netdev->ifname); netdev->ifname);
log_link_debug(link, "Requested to create stacked netdev '%s'", netdev->ifname); log_link_debug(link, "Requested stacked netdev '%s'", netdev->ifname);
return 0; return 0;
} }
int netdev_load_one(Manager *manager, const char *filename) { int netdev_load_one(Manager *manager, const char *filename) {
_cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL; _cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL;
const char *dropin_dirname; const char *dropin_dirname;
bool independent = false;
int r; int r;
assert(manager); assert(manager);
@ -792,48 +845,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
return r; return r;
} }
switch (netdev->kind) { if (netdev_is_stacked_and_independent(netdev)) {
case NETDEV_KIND_IPIP:
independent = IPIP(netdev)->independent;
break;
case NETDEV_KIND_GRE:
independent = GRE(netdev)->independent;
break;
case NETDEV_KIND_GRETAP:
independent = GRETAP(netdev)->independent;
break;
case NETDEV_KIND_IP6GRE:
independent = IP6GRE(netdev)->independent;
break;
case NETDEV_KIND_IP6GRETAP:
independent = IP6GRETAP(netdev)->independent;
break;
case NETDEV_KIND_SIT:
independent = SIT(netdev)->independent;
break;
case NETDEV_KIND_VTI:
independent = VTI(netdev)->independent;
break;
case NETDEV_KIND_VTI6:
independent = VTI6(netdev)->independent;
break;
case NETDEV_KIND_IP6TNL:
independent = IP6TNL(netdev)->independent;
break;
case NETDEV_KIND_ERSPAN:
independent = ERSPAN(netdev)->independent;
break;
case NETDEV_KIND_XFRM:
independent = XFRM(netdev)->independent;
break;
case NETDEV_KIND_VXLAN:
independent = VXLAN(netdev)->independent;
break;
default:
break;
}
if (independent) {
r = netdev_create(netdev, NULL, NULL); r = netdev_create(netdev, NULL, NULL);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -184,6 +184,7 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
int netdev_load(Manager *manager, bool reload); int netdev_load(Manager *manager, bool reload);
int netdev_load_one(Manager *manager, const char *filename); int netdev_load_one(Manager *manager, const char *filename);
void netdev_drop(NetDev *netdev); void netdev_drop(NetDev *netdev);
void netdev_enter_failed(NetDev *netdev);
NetDev *netdev_unref(NetDev *netdev); NetDev *netdev_unref(NetDev *netdev);
NetDev *netdev_ref(NetDev *netdev); NetDev *netdev_ref(NetDev *netdev);
@ -196,8 +197,8 @@ int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink);
int netdev_get_mac(const char *ifname, struct ether_addr **ret); int netdev_get_mac(const char *ifname, struct ether_addr **ret);
int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t cb); int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t cb);
int request_process_create_stacked_netdev(Request *req); int request_process_stacked_netdev(Request *req);
int link_request_to_crate_stacked_netdev(Link *link, NetDev *netdev); int link_request_stacked_netdev(Link *link, NetDev *netdev);
const char *netdev_kind_to_string(NetDevKind d) _const_; const char *netdev_kind_to_string(NetDevKind d) _const_;
NetDevKind netdev_kind_from_string(const char *d) _pure_; NetDevKind netdev_kind_from_string(const char *d) _pure_;

View File

@ -594,7 +594,7 @@ static int link_request_stacked_netdevs(Link *link) {
link->stacked_netdevs_after_configured_created = false; link->stacked_netdevs_after_configured_created = false;
HASHMAP_FOREACH(netdev, link->network->stacked_netdevs) { HASHMAP_FOREACH(netdev, link->network->stacked_netdevs) {
r = link_request_to_crate_stacked_netdev(link, netdev); r = link_request_stacked_netdev(link, netdev);
if (r < 0) if (r < 0)
return r; return r;
} }

View File

@ -32,8 +32,6 @@ static void request_free_object(RequestType type, void *object) {
case REQUEST_TYPE_BRIDGE_MDB: case REQUEST_TYPE_BRIDGE_MDB:
bridge_mdb_free(object); bridge_mdb_free(object);
break; break;
case REQUEST_TYPE_CREATE_STACKED_NETDEV:
break;
case REQUEST_TYPE_DHCP_SERVER: case REQUEST_TYPE_DHCP_SERVER:
case REQUEST_TYPE_DHCP4_CLIENT: case REQUEST_TYPE_DHCP4_CLIENT:
case REQUEST_TYPE_DHCP6_CLIENT: case REQUEST_TYPE_DHCP6_CLIENT:
@ -56,6 +54,7 @@ static void request_free_object(RequestType type, void *object) {
routing_policy_rule_free(object); routing_policy_rule_free(object);
break; break;
case REQUEST_TYPE_SET_LINK: case REQUEST_TYPE_SET_LINK:
case REQUEST_TYPE_STACKED_NETDEV:
case REQUEST_TYPE_UP_DOWN: case REQUEST_TYPE_UP_DOWN:
break; break;
default: default:
@ -110,7 +109,7 @@ static void request_hash_func(const Request *req, struct siphash *state) {
case REQUEST_TYPE_ADDRESS_LABEL: case REQUEST_TYPE_ADDRESS_LABEL:
case REQUEST_TYPE_BRIDGE_FDB: case REQUEST_TYPE_BRIDGE_FDB:
case REQUEST_TYPE_BRIDGE_MDB: case REQUEST_TYPE_BRIDGE_MDB:
case REQUEST_TYPE_CREATE_STACKED_NETDEV: case REQUEST_TYPE_STACKED_NETDEV:
/* TODO: Currently, these types do not have any specific hash and compare functions. /* TODO: Currently, these types do not have any specific hash and compare functions.
* Fortunately, all these objects are 'static', thus we can use the trivial functions. */ * Fortunately, all these objects are 'static', thus we can use the trivial functions. */
trivial_hash_func(req->object, state); trivial_hash_func(req->object, state);
@ -173,7 +172,7 @@ static int request_compare_func(const struct Request *a, const struct Request *b
case REQUEST_TYPE_ADDRESS_LABEL: case REQUEST_TYPE_ADDRESS_LABEL:
case REQUEST_TYPE_BRIDGE_FDB: case REQUEST_TYPE_BRIDGE_FDB:
case REQUEST_TYPE_BRIDGE_MDB: case REQUEST_TYPE_BRIDGE_MDB:
case REQUEST_TYPE_CREATE_STACKED_NETDEV: case REQUEST_TYPE_STACKED_NETDEV:
return trivial_compare_func(a->object, b->object); return trivial_compare_func(a->object, b->object);
case REQUEST_TYPE_DHCP_SERVER: case REQUEST_TYPE_DHCP_SERVER:
case REQUEST_TYPE_DHCP4_CLIENT: case REQUEST_TYPE_DHCP4_CLIENT:
@ -306,9 +305,6 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
case REQUEST_TYPE_BRIDGE_MDB: case REQUEST_TYPE_BRIDGE_MDB:
r = request_process_bridge_mdb(req); r = request_process_bridge_mdb(req);
break; break;
case REQUEST_TYPE_CREATE_STACKED_NETDEV:
r = request_process_create_stacked_netdev(req);
break;
case REQUEST_TYPE_DHCP_SERVER: case REQUEST_TYPE_DHCP_SERVER:
r = request_process_dhcp_server(req); r = request_process_dhcp_server(req);
break; break;
@ -339,6 +335,9 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
case REQUEST_TYPE_SET_LINK: case REQUEST_TYPE_SET_LINK:
r = request_process_set_link(req); r = request_process_set_link(req);
break; break;
case REQUEST_TYPE_STACKED_NETDEV:
r = request_process_stacked_netdev(req);
break;
case REQUEST_TYPE_UP_DOWN: case REQUEST_TYPE_UP_DOWN:
r = request_process_link_up_or_down(req); r = request_process_link_up_or_down(req);
break; break;

View File

@ -26,7 +26,6 @@ typedef enum RequestType {
REQUEST_TYPE_ADDRESS_LABEL, REQUEST_TYPE_ADDRESS_LABEL,
REQUEST_TYPE_BRIDGE_FDB, REQUEST_TYPE_BRIDGE_FDB,
REQUEST_TYPE_BRIDGE_MDB, REQUEST_TYPE_BRIDGE_MDB,
REQUEST_TYPE_CREATE_STACKED_NETDEV,
REQUEST_TYPE_DHCP_SERVER, REQUEST_TYPE_DHCP_SERVER,
REQUEST_TYPE_DHCP4_CLIENT, REQUEST_TYPE_DHCP4_CLIENT,
REQUEST_TYPE_DHCP6_CLIENT, REQUEST_TYPE_DHCP6_CLIENT,
@ -37,6 +36,7 @@ typedef enum RequestType {
REQUEST_TYPE_ROUTE, REQUEST_TYPE_ROUTE,
REQUEST_TYPE_ROUTING_POLICY_RULE, REQUEST_TYPE_ROUTING_POLICY_RULE,
REQUEST_TYPE_SET_LINK, REQUEST_TYPE_SET_LINK,
REQUEST_TYPE_STACKED_NETDEV,
REQUEST_TYPE_UP_DOWN, REQUEST_TYPE_UP_DOWN,
_REQUEST_TYPE_MAX, _REQUEST_TYPE_MAX,
_REQUEST_TYPE_INVALID = -EINVAL, _REQUEST_TYPE_INVALID = -EINVAL,

View File

@ -362,9 +362,8 @@ void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level
if (s->possible_feature_level > level) { if (s->possible_feature_level > level) {
s->possible_feature_level = level; s->possible_feature_level = level;
dns_server_reset_counters(s); dns_server_reset_counters(s);
log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s)));
} }
log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s)));
} }
void dns_server_packet_invalid(DnsServer *s, DnsServerFeatureLevel level) { void dns_server_packet_invalid(DnsServer *s, DnsServerFeatureLevel level) {

View File

@ -1142,22 +1142,35 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt
break; break;
} }
/* Reduce this feature level by one and try again. */ /* SERVFAIL can happen for many reasons and may be transient.
switch (t->current_feature_level) { * To avoid unnecessary downgrades retry once with the initial level.
case DNS_SERVER_FEATURE_LEVEL_TLS_DO: * Check for clamp_feature_level_servfail having an invalid value as a sign that this is the
t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN; * first attempt to downgrade. If so, clamp to the current value so that the transaction
break; * is retried without actually downgrading. If the next try also fails we will downgrade by
case DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN + 1: * hitting the else branch below. */
/* Skip plain TLS when TLS is not supported */ if (DNS_PACKET_RCODE(p) == DNS_RCODE_SERVFAIL &&
t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN - 1; t->clamp_feature_level_servfail < 0) {
break; t->clamp_feature_level_servfail = t->current_feature_level;
default: log_debug("Server returned error %s, retrying transaction.",
t->clamp_feature_level_servfail = t->current_feature_level - 1; dns_rcode_to_string(DNS_PACKET_RCODE(p)));
} } else {
/* Reduce this feature level by one and try again. */
switch (t->current_feature_level) {
case DNS_SERVER_FEATURE_LEVEL_TLS_DO:
t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN;
break;
case DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN + 1:
/* Skip plain TLS when TLS is not supported */
t->clamp_feature_level_servfail = DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN - 1;
break;
default:
t->clamp_feature_level_servfail = t->current_feature_level - 1;
}
log_debug("Server returned error %s, retrying transaction with reduced feature level %s.", log_debug("Server returned error %s, retrying transaction with reduced feature level %s.",
dns_rcode_to_string(DNS_PACKET_RCODE(p)), dns_rcode_to_string(DNS_PACKET_RCODE(p)),
dns_server_feature_level_to_string(t->clamp_feature_level_servfail)); dns_server_feature_level_to_string(t->clamp_feature_level_servfail));
}
dns_transaction_retry(t, false /* use the same server */); dns_transaction_retry(t, false /* use the same server */);
return; return;

View File

@ -7,6 +7,8 @@
/* Do some basic checks on STRLEN() and DECIMAL_STR_MAX() */ /* Do some basic checks on STRLEN() and DECIMAL_STR_MAX() */
assert_cc(STRLEN("xxx") == 3); assert_cc(STRLEN("xxx") == 3);
assert_cc(STRLEN("") == 0); assert_cc(STRLEN("") == 0);
assert_cc(STRLEN(L"xxx") == 3 * sizeof(wchar_t));
assert_cc(STRLEN(L"") == 0);
assert_cc(DECIMAL_STR_MAX(uint8_t) == 5); assert_cc(DECIMAL_STR_MAX(uint8_t) == 5);
assert_cc(DECIMAL_STR_MAX(int8_t) == 5); assert_cc(DECIMAL_STR_MAX(int8_t) == 5);
assert_cc(DECIMAL_STR_MAX(uint64_t) == 22); assert_cc(DECIMAL_STR_MAX(uint64_t) == 22);

View File

@ -1183,6 +1183,25 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
self.assertRegex(output, ' mtu 2000 ') self.assertRegex(output, ' mtu 2000 ')
self.assertRegex(output, 'macvlan mode ' + mode + ' ') self.assertRegex(output, 'macvlan mode ' + mode + ' ')
rc = call("ip link del test1")
self.assertEqual(rc, 0)
time.sleep(1)
rc = call("ip link add test1 type dummy")
self.assertEqual(rc, 0)
time.sleep(1)
self.wait_online(['macvlan99:degraded', 'test1:degraded'])
output = check_output('ip -d link show test1')
print(output)
self.assertRegex(output, ' mtu 2000 ')
output = check_output('ip -d link show macvlan99')
print(output)
self.assertRegex(output, ' mtu 2000 ')
self.assertRegex(output, 'macvlan mode ' + mode + ' ')
@expectedFailureIfModuleIsNotAvailable('ipvlan') @expectedFailureIfModuleIsNotAvailable('ipvlan')
def test_ipvlan(self): def test_ipvlan(self):
for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]: for mode, flag in [['L2', 'private'], ['L3', 'vepa'], ['L3S', 'bridge']]: