2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-10 19:46:24 +00:00

fix(grub2): keep the whole kernel command line past a grub2 separator

grub2 reads its configuration as a script, so an unquoted command separator
ends the linux command and everything after it is lost. The Ubuntu
installer seed is written as ds=nocloud-net;s=<url>, so the node booted
without the seed URL and without the arguments that followed it, including
BOOTIF. The installer then found no autoinstall configuration and waited
for someone to answer its questions. A separator that is neither escaped
nor inside a quoted span is now escaped where it stands, which grub2
removes before it hands the line to the kernel. A value the caller escaped
or quoted keeps exactly the form the caller gave it.

Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
This commit is contained in:
Vinícius Ferrão
2026-09-05 01:49:31 -03:00
parent 7fed9001e8
commit 52e468884e
+25 -1
View File
@@ -85,6 +85,29 @@ sub getstate {
}
}
# grub2 reads its configuration as a script, so an unquoted word carrying one of the
# characters below ends the linux command and the rest of the kernel command line is lost.
# The Ubuntu installer seed (ds=nocloud-net;s=<url>) is the usual casualty.
my $GRUB2_TERMINATOR = qr/[;{}|&<>()]/;
sub quote_kcmdline {
my $kcmdline = shift;
return $kcmdline unless (defined $kcmdline and $kcmdline =~ $GRUB2_TERMINATOR);
# Escaped in place rather than quoted as a whole: a value the caller quoted keeps the
# quoting it was given, which grub2 removes before the kernel sees the value.
my $escaped = '';
while (length $kcmdline) {
if ($kcmdline =~ s/^('[^']*'|"[^"]*")//) { $escaped .= $1; next; }
if ($kcmdline =~ s/^(\\.)//) { $escaped .= $1; next; }
$kcmdline =~ s/^(.)//s;
my $char = $1;
$escaped .= ($char =~ $GRUB2_TERMINATOR) ? "\\$char" : $char;
}
return $escaped;
}
sub setstate {
=pod
@@ -260,7 +283,8 @@ sub setstate {
}
if ($kern and $kern->{kcmdline}) {
print $pcfg " linux$efi $protocolrootdir/$kern->{kernel} $kern->{kcmdline} BOOTIF=\$net_default_mac\n";
my $kcmdline = quote_kcmdline($kern->{kcmdline});
print $pcfg " linux$efi $protocolrootdir/$kern->{kernel} $kcmdline BOOTIF=\$net_default_mac\n";
} else {
print $pcfg " linux$efi $protocolrootdir/$kern->{kernel} BOOTIF=\$net_default_mac\n";
}