Version in base suite: 2.12-9+deb13u2 Base version: grub2_2.12-9+deb13u2 Target version: grub2_2.12-9+deb13u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/g/grub2/grub2_2.12-9+deb13u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/g/grub2/grub2_2.12-9+deb13u3.dsc changelog | 24 patches/cve-2025-nov/commands-test-Fix-error-in-recursion-depth-calculati.patch | 35 + patches/cve-2025-nov/commands-usbtest-Ensure-string-length-is-sufficient-.patch | 33 + patches/cve-2025-nov/commands-usbtest-Use-correct-string-length-field.patch | 35 + patches/cve-2025-nov/gettext-gettext-Unregister-gettext-command-on-module.patch | 66 ++ patches/cve-2025-nov/kern-file-Call-grub_dl_unref-after-fs-fs_close.patch | 46 + patches/cve-2025-nov/net-net-Unregister-net_set_vlan-command-on-unload.patch | 36 + patches/cve-2025-nov/normal-main-Unregister-commands-on-module-unload.patch | 59 ++ patches/cve-2025-nov/tests-lib-functional_test-Unregister-commands-on-mod.patch | 47 + patches/cve-2026-sep/docs-grub-Document-serial-port-restrictions-under-lockdow.patch | 62 ++ patches/cve-2026-sep/term-serial-Reject-caller-supplied-port-addresses-under-l.patch | 263 ++++++++++ patches/series | 10 sbat.debian.csv.in | 2 13 files changed, 717 insertions(+), 1 deletion(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpobsb20qz/grub2_2.12-9+deb13u2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpobsb20qz/grub2_2.12-9+deb13u3.dsc: no acceptable signature found diff -Nru grub2-2.12/debian/changelog grub2-2.12/debian/changelog --- grub2-2.12/debian/changelog 2026-04-30 19:02:01.000000000 +0000 +++ grub2-2.12/debian/changelog 2026-11-05 12:16:56.000000000 +0000 @@ -1,3 +1,27 @@ +grub2 (2.12-9+deb13u3) trixie; urgency=medium + + [ Fabian Grünbichler ] + * Patches for November 2025 CVEs + + CVE-2025-54770: a use-after-free issue in the network module's + VLAN handling code that can be used to trigger a denial of + service + + CVE-2025-54771: a use-after-free issue when closing a file which + can be used to trigger at least a denial of service + + CVE-2025-61661: mishandling of string conversion when reading data from a + (malicious) USB device, which can be used to trigger at least a denial of + service + + CVE-2025-61662: a use-after-free issue in the gettext module, + which can be used to trigger at least a denial of service + + CVE-2025-61663/CVE-2025-61664: two use-after-free issues when + unloading the `normal` module, which can be used to trigger at + least a denial of service + + Bump SBAT to grub,6 to match. + * Patches for September 2026 CVE + + CVE-2026-97876: serial MMIO secure boot bypass + + Bump SBAT to grub,7 to match. + + -- Steve McIntyre <93sam@debian.org> Thu, 05 Nov 2026 12:17:08 +0000 + grub2 (2.12-9+deb13u2) trixie; urgency=medium * Non-maintainer upload. diff -Nru grub2-2.12/debian/patches/cve-2025-nov/commands-test-Fix-error-in-recursion-depth-calculati.patch grub2-2.12/debian/patches/cve-2025-nov/commands-test-Fix-error-in-recursion-depth-calculati.patch --- grub2-2.12/debian/patches/cve-2025-nov/commands-test-Fix-error-in-recursion-depth-calculati.patch 1970-01-01 00:00:00.000000000 +0000 +++ grub2-2.12/debian/patches/cve-2025-nov/commands-test-Fix-error-in-recursion-depth-calculati.patch 2026-11-05 12:04:01.000000000 +0000 @@ -0,0 +1,35 @@ +From: Thomas Frauendorfer | Miray Software +Date: Fri, 9 May 2025 13:51:08 +0200 +Subject: commands/test: Fix error in recursion depth calculation +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +The commit c68b7d236 (commands/test: Stack overflow due to unlimited +recursion depth) added recursion depth tests to the test command. But in +the error case it decrements the pointer to the depth value instead of +the value itself. Fix it. + +Fixes: c68b7d236 (commands/test: Stack overflow due to unlimited recursion depth) + +Signed-off-by: Thomas Frauendorfer | Miray Software +Reviewed-by: Daniel Kiper +(cherry picked from commit cc9d621dd06bfa12eac511b37b4ceda5bd2f8246) +Signed-off-by: Fabian Grünbichler +--- + grub-core/commands/test.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/grub-core/commands/test.c b/grub-core/commands/test.c +index b585c3d..ee47ab2 100644 +--- a/grub-core/commands/test.c ++++ b/grub-core/commands/test.c +@@ -403,7 +403,7 @@ test_parse (char **args, int *argn, int argc, int *depth) + if (++(*depth) > MAX_TEST_RECURSION_DEPTH) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("max recursion depth exceeded")); +- depth--; ++ (*depth)--; + return ctx.or || ctx.and; + } + diff -Nru grub2-2.12/debian/patches/cve-2025-nov/commands-usbtest-Ensure-string-length-is-sufficient-.patch grub2-2.12/debian/patches/cve-2025-nov/commands-usbtest-Ensure-string-length-is-sufficient-.patch --- grub2-2.12/debian/patches/cve-2025-nov/commands-usbtest-Ensure-string-length-is-sufficient-.patch 1970-01-01 00:00:00.000000000 +0000 +++ grub2-2.12/debian/patches/cve-2025-nov/commands-usbtest-Ensure-string-length-is-sufficient-.patch 2026-11-05 12:04:01.000000000 +0000 @@ -0,0 +1,33 @@ +From: Jamie +Date: Mon, 14 Jul 2025 10:07:47 +0100 +Subject: commands/usbtest: Ensure string length is sufficient in usb string + processing +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +If descstrp->length is less than 2 this will result in underflow in +"descstrp->length / 2 - 1" math. Let's fix the check to make sure the +value is sufficient. + +Signed-off-by: Jamie +Reviewed-by: Daniel Kiper +(cherry picked from commit 7debdce1e98907e65223a4b4c53a41345ac45e53) +Signed-off-by: Fabian Grünbichler +--- + grub-core/commands/usbtest.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/grub-core/commands/usbtest.c b/grub-core/commands/usbtest.c +index 8ef187a..3184ac9 100644 +--- a/grub-core/commands/usbtest.c ++++ b/grub-core/commands/usbtest.c +@@ -90,7 +90,7 @@ grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid, + 0x06, (3 << 8) | index, + langid, descstr.length, (char *) descstrp); + +- if (descstrp->length == 0) ++ if (descstrp->length < 2) + { + grub_free (descstrp); + *string = grub_strdup (""); diff -Nru grub2-2.12/debian/patches/cve-2025-nov/commands-usbtest-Use-correct-string-length-field.patch grub2-2.12/debian/patches/cve-2025-nov/commands-usbtest-Use-correct-string-length-field.patch --- grub2-2.12/debian/patches/cve-2025-nov/commands-usbtest-Use-correct-string-length-field.patch 1970-01-01 00:00:00.000000000 +0000 +++ grub2-2.12/debian/patches/cve-2025-nov/commands-usbtest-Use-correct-string-length-field.patch 2026-11-05 12:04:01.000000000 +0000 @@ -0,0 +1,35 @@ +From: Jamie +Date: Mon, 14 Jul 2025 09:52:59 +0100 +Subject: commands/usbtest: Use correct string length field +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +An incorrect length field is used for buffer allocation. This leads to +grub_utf16_to_utf8() receiving an incorrect/different length and possibly +causing OOB write. This makes sure to use the correct length. + +Fixes: CVE-2025-61661 + +Reported-by: Jamie +Signed-off-by: Jamie +Reviewed-by: Daniel Kiper +(cherry picked from commit 549a9cc372fd0b96a4ccdfad0e12140476cc62a3) +Signed-off-by: Fabian Grünbichler +--- + grub-core/commands/usbtest.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/grub-core/commands/usbtest.c b/grub-core/commands/usbtest.c +index 2c6d93f..8ef187a 100644 +--- a/grub-core/commands/usbtest.c ++++ b/grub-core/commands/usbtest.c +@@ -99,7 +99,7 @@ grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid, + return GRUB_USB_ERR_NONE; + } + +- *string = grub_malloc (descstr.length * 2 + 1); ++ *string = grub_malloc (descstrp->length * 2 + 1); + if (! *string) + { + grub_free (descstrp); diff -Nru grub2-2.12/debian/patches/cve-2025-nov/gettext-gettext-Unregister-gettext-command-on-module.patch grub2-2.12/debian/patches/cve-2025-nov/gettext-gettext-Unregister-gettext-command-on-module.patch --- grub2-2.12/debian/patches/cve-2025-nov/gettext-gettext-Unregister-gettext-command-on-module.patch 1970-01-01 00:00:00.000000000 +0000 +++ grub2-2.12/debian/patches/cve-2025-nov/gettext-gettext-Unregister-gettext-command-on-module.patch 2026-11-05 12:04:01.000000000 +0000 @@ -0,0 +1,66 @@ +From: Alec Brown +Date: Thu, 21 Aug 2025 21:14:06 +0000 +Subject: gettext/gettext: Unregister gettext command on module unload +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +When the gettext module is loaded, the gettext command is registered but +isn't unregistered when the module is unloaded. We need to add a call to +grub_unregister_command() when unloading the module. + +Fixes: CVE-2025-61662 + +Reported-by: Alec Brown +Signed-off-by: Alec Brown +Reviewed-by: Daniel Kiper +(cherry picked from commit 8ed78fd9f0852ab218cc1f991c38e5a229e43807) +Signed-off-by: Fabian Grünbichler +--- + grub-core/gettext/gettext.c | 19 ++++++++++++------- + 1 file changed, 12 insertions(+), 7 deletions(-) + +diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c +index f6a9f5a..cf4c75e 100644 +--- a/grub-core/gettext/gettext.c ++++ b/grub-core/gettext/gettext.c +@@ -507,6 +507,8 @@ grub_cmd_translate (grub_command_t cmd __attribute__ ((unused)), + return 0; + } + ++static grub_command_t cmd; ++ + GRUB_MOD_INIT (gettext) + { + const char *lang; +@@ -526,13 +528,14 @@ GRUB_MOD_INIT (gettext) + grub_register_variable_hook ("locale_dir", NULL, read_main); + grub_register_variable_hook ("secondary_locale_dir", NULL, read_secondary); + +- grub_register_command_p1 ("gettext", grub_cmd_translate, +- N_("STRING"), +- /* TRANSLATORS: It refers to passing the string through gettext. +- So it's "translate" in the same meaning as in what you're +- doing now. +- */ +- N_("Translates the string with the current settings.")); ++ cmd = grub_register_command_p1 ("gettext", grub_cmd_translate, ++ N_("STRING"), ++ /* ++ * TRANSLATORS: It refers to passing the string through gettext. ++ * So it's "translate" in the same meaning as in what you're ++ * doing now. ++ */ ++ N_("Translates the string with the current settings.")); + + /* Reload .mo file information if lang changes. */ + grub_register_variable_hook ("lang", NULL, grub_gettext_env_write_lang); +@@ -549,6 +552,8 @@ GRUB_MOD_FINI (gettext) + grub_register_variable_hook ("secondary_locale_dir", NULL, NULL); + grub_register_variable_hook ("lang", NULL, NULL); + ++ grub_unregister_command (cmd); ++ + grub_gettext_delete_list (&main_context); + grub_gettext_delete_list (&secondary_context); + diff -Nru grub2-2.12/debian/patches/cve-2025-nov/kern-file-Call-grub_dl_unref-after-fs-fs_close.patch grub2-2.12/debian/patches/cve-2025-nov/kern-file-Call-grub_dl_unref-after-fs-fs_close.patch --- grub2-2.12/debian/patches/cve-2025-nov/kern-file-Call-grub_dl_unref-after-fs-fs_close.patch 1970-01-01 00:00:00.000000000 +0000 +++ grub2-2.12/debian/patches/cve-2025-nov/kern-file-Call-grub_dl_unref-after-fs-fs_close.patch 2026-11-05 12:04:01.000000000 +0000 @@ -0,0 +1,46 @@ +From: Thomas Frauendorfer | Miray Software +Date: Wed, 7 May 2025 16:15:22 +0200 +Subject: kern/file: Call grub_dl_unref() after fs->fs_close() +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +With commit 16f196874 (kern/file: Implement filesystem reference +counting) files hold a reference to their file systems. + +When closing a file in grub_file_close() we should not expect +file->fs to stay valid after calling grub_dl_unref() on file->fs->mod. +So, grub_dl_unref() should be called after file->fs->fs_close(). + +Fixes: CVE-2025-54771 +Fixes: 16f196874 (kern/file: Implement filesystem reference counting) + +Reported-by: Thomas Frauendorfer | Miray Software +Signed-off-by: Thomas Frauendorfer | Miray Software +Reviewed-by: Daniel Kiper +(cherry picked from commit c4fb4cbc941981894a00ba8e75d634a41967a27f) +Signed-off-by: Fabian Grünbichler +--- + grub-core/kern/file.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c +index 6e7efe8..eb52fd2 100644 +--- a/grub-core/kern/file.c ++++ b/grub-core/kern/file.c +@@ -201,12 +201,12 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len) + grub_err_t + grub_file_close (grub_file_t file) + { +- if (file->fs->mod) +- grub_dl_unref (file->fs->mod); +- + if (file->fs->fs_close) + (file->fs->fs_close) (file); + ++ if (file->fs->mod) ++ grub_dl_unref (file->fs->mod); ++ + if (file->device) + grub_device_close (file->device); + grub_free (file->name); diff -Nru grub2-2.12/debian/patches/cve-2025-nov/net-net-Unregister-net_set_vlan-command-on-unload.patch grub2-2.12/debian/patches/cve-2025-nov/net-net-Unregister-net_set_vlan-command-on-unload.patch --- grub2-2.12/debian/patches/cve-2025-nov/net-net-Unregister-net_set_vlan-command-on-unload.patch 1970-01-01 00:00:00.000000000 +0000 +++ grub2-2.12/debian/patches/cve-2025-nov/net-net-Unregister-net_set_vlan-command-on-unload.patch 2026-11-05 12:04:01.000000000 +0000 @@ -0,0 +1,36 @@ +From: Thomas Frauendorfer | Miray Software +Date: Fri, 9 May 2025 14:20:47 +0200 +Subject: net/net: Unregister net_set_vlan command on unload +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +The commit 954c48b9c (net/net: Add net_set_vlan command) added command +net_set_vlan to the net module. Unfortunately the commit only added the +grub_register_command() call on module load but missed the +grub_unregister_command() on unload. Let's fix this. + +Fixes: CVE-2025-54770 +Fixes: 954c48b9c (net/net: Add net_set_vlan command) + +Reported-by: Thomas Frauendorfer | Miray Software +Signed-off-by: Thomas Frauendorfer | Miray Software +Reviewed-by: Daniel Kiper +(cherry picked from commit 10e58a14db20e17d1b6a39abe38df01fef98e29d) +Signed-off-by: Fabian Grünbichler +--- + grub-core/net/net.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/grub-core/net/net.c b/grub-core/net/net.c +index 7ec4823..864c125 100644 +--- a/grub-core/net/net.c ++++ b/grub-core/net/net.c +@@ -2289,6 +2289,7 @@ GRUB_MOD_FINI(net) + grub_unregister_command (cmd_deladdr); + grub_unregister_command (cmd_addroute); + grub_unregister_command (cmd_delroute); ++ grub_unregister_command (cmd_setvlan); + grub_unregister_command (cmd_lsroutes); + grub_unregister_command (cmd_lscards); + grub_unregister_command (cmd_lsaddr); diff -Nru grub2-2.12/debian/patches/cve-2025-nov/normal-main-Unregister-commands-on-module-unload.patch grub2-2.12/debian/patches/cve-2025-nov/normal-main-Unregister-commands-on-module-unload.patch --- grub2-2.12/debian/patches/cve-2025-nov/normal-main-Unregister-commands-on-module-unload.patch 1970-01-01 00:00:00.000000000 +0000 +++ grub2-2.12/debian/patches/cve-2025-nov/normal-main-Unregister-commands-on-module-unload.patch 2026-11-05 12:04:01.000000000 +0000 @@ -0,0 +1,59 @@ +From: Alec Brown +Date: Thu, 21 Aug 2025 21:14:07 +0000 +Subject: normal/main: Unregister commands on module unload +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +When the normal module is loaded, the normal and normal_exit commands +are registered but aren't unregistered when the module is unloaded. We +need to add calls to grub_unregister_command() when unloading the module +for these commands. + +Fixes: CVE-2025-61663 +Fixes: CVE-2025-61664 + +Reported-by: Alec Brown +Signed-off-by: Alec Brown +Reviewed-by: Daniel Kiper +(cherry picked from commit 05d3698b8b03eccc49e53491bbd75dba15f40917) +Signed-off-by: Fabian Grünbichler +--- + grub-core/normal/main.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c +index 527b249..ed45ee3 100644 +--- a/grub-core/normal/main.c ++++ b/grub-core/normal/main.c +@@ -582,7 +582,7 @@ grub_mini_cmd_clear (struct grub_command *cmd __attribute__ ((unused)), + return 0; + } + +-static grub_command_t cmd_clear; ++static grub_command_t cmd_clear, cmd_normal, cmd_normal_exit; + + static void (*grub_xputs_saved) (const char *str); + static const char *features[] = { +@@ -624,10 +624,10 @@ GRUB_MOD_INIT(normal) + grub_env_export ("pager"); + + /* Register a command "normal" for the rescue mode. */ +- grub_register_command ("normal", grub_cmd_normal, +- 0, N_("Enter normal mode.")); +- grub_register_command ("normal_exit", grub_cmd_normal_exit, +- 0, N_("Exit from normal mode.")); ++ cmd_normal = grub_register_command ("normal", grub_cmd_normal, ++ 0, N_("Enter normal mode.")); ++ cmd_normal_exit = grub_register_command ("normal_exit", grub_cmd_normal_exit, ++ 0, N_("Exit from normal mode.")); + + /* Reload terminal colors when these variables are written to. */ + grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal); +@@ -669,4 +669,6 @@ GRUB_MOD_FINI(normal) + grub_register_variable_hook ("color_highlight", NULL, NULL); + grub_fs_autoload_hook = 0; + grub_unregister_command (cmd_clear); ++ grub_unregister_command (cmd_normal); ++ grub_unregister_command (cmd_normal_exit); + } diff -Nru grub2-2.12/debian/patches/cve-2025-nov/tests-lib-functional_test-Unregister-commands-on-mod.patch grub2-2.12/debian/patches/cve-2025-nov/tests-lib-functional_test-Unregister-commands-on-mod.patch --- grub2-2.12/debian/patches/cve-2025-nov/tests-lib-functional_test-Unregister-commands-on-mod.patch 1970-01-01 00:00:00.000000000 +0000 +++ grub2-2.12/debian/patches/cve-2025-nov/tests-lib-functional_test-Unregister-commands-on-mod.patch 2026-11-05 12:04:01.000000000 +0000 @@ -0,0 +1,47 @@ +From: Alec Brown +Date: Thu, 21 Aug 2025 21:14:08 +0000 +Subject: tests/lib/functional_test: Unregister commands on module unload +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +When the functional_test module is loaded, both the functional_test and +all_functional_test commands are registered but only the all_functional_test +command is being unregistered since it was the last to set the cmd variable +that gets unregistered when the module is unloaded. To unregister both +commands, we need to create an additional grub_extcmd_t variable. + +Signed-off-by: Alec Brown +Reviewed-by: Daniel Kiper +(cherry picked from commit 9df1e693e70c5a274b6d60dc76efe2694b89c2fc) +Signed-off-by: Fabian Grünbichler +--- + grub-core/tests/lib/functional_test.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c +index 96781fb..b0f6845 100644 +--- a/grub-core/tests/lib/functional_test.c ++++ b/grub-core/tests/lib/functional_test.c +@@ -89,17 +89,18 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)), + return GRUB_ERR_NONE; + } + +-static grub_extcmd_t cmd; ++static grub_extcmd_t cmd, cmd_all; + + GRUB_MOD_INIT (functional_test) + { + cmd = grub_register_extcmd ("functional_test", grub_functional_test, 0, 0, + "Run all loaded functional tests.", 0); +- cmd = grub_register_extcmd ("all_functional_test", grub_functional_all_tests, 0, 0, +- "Run all functional tests.", 0); ++ cmd_all = grub_register_extcmd ("all_functional_test", grub_functional_all_tests, 0, 0, ++ "Run all functional tests.", 0); + } + + GRUB_MOD_FINI (functional_test) + { + grub_unregister_extcmd (cmd); ++ grub_unregister_extcmd (cmd_all); + } diff -Nru grub2-2.12/debian/patches/cve-2026-sep/docs-grub-Document-serial-port-restrictions-under-lockdow.patch grub2-2.12/debian/patches/cve-2026-sep/docs-grub-Document-serial-port-restrictions-under-lockdow.patch --- grub2-2.12/debian/patches/cve-2026-sep/docs-grub-Document-serial-port-restrictions-under-lockdow.patch 1970-01-01 00:00:00.000000000 +0000 +++ grub2-2.12/debian/patches/cve-2026-sep/docs-grub-Document-serial-port-restrictions-under-lockdow.patch 2026-11-05 12:04:01.000000000 +0000 @@ -0,0 +1,62 @@ +From: Andrew Hamilton +Date: Tue, 15 Sep 2026 17:45:30 -0500 +Subject: docs/grub: Document serial port restrictions under lockdown + +Document that the serial command with the raw address is restricted +in lockdown mode to avoid misuse by an attacker (except for if the +given address is already associated with a platform provided +device). + +Assisted-by: Claude:Opus-5 + +Signed-off-by: Andrew Hamilton +Reviewed-by: Leo Sandoval +Part-of: +(cherry picked from commit 92a2f034ed94ecdf005095e5e645d8904a8403a5) +--- + docs/grub.texi | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/docs/grub.texi b/docs/grub.texi +index b4825b7..097d8d2 100644 +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -4270,6 +4270,38 @@ Note, valid @var{port} values, excluding IO port addresses, can be found + by listing terminals with @command{terminal_output}, selecting all names + prefixed by @samp{serial_} and removing that prefix. + ++When lockdown is enforced (@pxref{Lockdown}) a @var{port} that gives a raw ++I/O or MMIO address is accepted only if a port is already registered at that ++address, which means the platform described that UART. An address nothing is ++registered at is rejected, because nothing in the UART programming sequence ++can tell a real UART from ordinary memory and the configuration supplying the ++address is not authenticated. Matching is on the address, so it does not ++matter how the address is spelled, and the registered port keeps the access ++size the platform gave it. ++ ++The ports a platform describes are: ++ ++@itemize @bullet ++@item ++the @samp{com@var{N}} legacy UARTs, named by @var{unit} or by their I/O ++address (x86 only) ++@item ++an ACPI SPCR UART, which @command{serial auto} -- or @command{serial} with no ++options at all -- parses the table for (x86 only). Note that ++@option{--port=auto} does @emph{not} select this: @option{--port} parses its ++argument as a number unless it begins with @samp{mmio,} or @samp{pci,}, so ++@samp{auto} there becomes port 0. ++@item ++a UEFI Serial I/O device, named @samp{efi@var{N}}. On platforms with no ++legacy UART and no SPCR support, such as arm64, this is the route to use. ++@item ++an enumerated PCI device, named @samp{pci,XX:XX.X} or by its I/O address ++@end itemize ++ ++An SPCR UART is registered only when @command{serial auto} first parses the ++table, so under lockdown it must be reached through @command{serial auto} ++once before it can be named by its address. ++ + Examples: + @example + serial --port=0x3f8 --speed=9600 diff -Nru grub2-2.12/debian/patches/cve-2026-sep/term-serial-Reject-caller-supplied-port-addresses-under-l.patch grub2-2.12/debian/patches/cve-2026-sep/term-serial-Reject-caller-supplied-port-addresses-under-l.patch --- grub2-2.12/debian/patches/cve-2026-sep/term-serial-Reject-caller-supplied-port-addresses-under-l.patch 1970-01-01 00:00:00.000000000 +0000 +++ grub2-2.12/debian/patches/cve-2026-sep/term-serial-Reject-caller-supplied-port-addresses-under-l.patch 2026-11-05 12:04:01.000000000 +0000 @@ -0,0 +1,263 @@ +From: Andrew Hamilton +Date: Tue, 15 Sep 2026 17:49:10 -0500 +Subject: term/serial: Reject caller-supplied port addresses under lockdown +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +The serial command is available in lockdown mode and the port option +accepts a raw port address or a raw mmio base address. This can be +misused to allow an attacker to write to local memory and do things +including disabling / bypassing secure boot as long as the +console is available or an unsigned GRUB configuration is under +the attacker's control. + +Update the serial logic to only accept a raw address in lockdown when +a port is already registered at it, which means the platform described +that UART: grub_ns8250_init() for the legacy com ports, +grub_pciserial_init() for an enumerated PCI device, or an ACPI SPCR +UART once "auto" has parsed the table. Names that are not addresses +are untouched, so "auto", efi from grub_efiserial_init() - the +route on arm64, which has neither ns8250 nor SPCR support - and +lookups by registered name all keep working. + +Behavior without lockdown (no secure boot) is unchanged. + +Assisted-by: Claude:Opus-5 + +Signed-off-by: Andrew Hamilton +Suggested-by: Benjamin Herrenschmidt +Reviewed-by: Leo Sandoval +Part-of: +(cherry picked from commit 26beaa3b2720fefdc4d04c1ae209b776fe6848d6) + +CVE-2026-97876 according to https://github.com/rhboot/shim/pull/830/ + +Conflicts: + grub-core/term/pl011.c + grub-core/term/serial.c + include/grub/pl011.h + +the pl011 serial port for arm* does not exist yet in 2.12, dropping the hunks +relating to it accordingly. + +Signed-off-by: Fabian Grünbichler +--- + grub-core/term/ns8250-spcr.c | 6 ++++-- + grub-core/term/ns8250.c | 27 ++++++++++++++++++++---- + grub-core/term/serial.c | 50 +++++++++++++++++++++++++++++++++++++++----- + include/grub/serial.h | 8 +++++-- + 4 files changed, 78 insertions(+), 13 deletions(-) + +diff --git a/grub-core/term/ns8250-spcr.c b/grub-core/term/ns8250-spcr.c +index d52b52c..ae20b07 100644 +--- a/grub-core/term/ns8250-spcr.c ++++ b/grub-core/term/ns8250-spcr.c +@@ -77,11 +77,13 @@ grub_ns8250_spcr_init (void) + }; + switch (spcr->base_addr.space_id) + { ++ /* The address comes from the platform's own ACPI table, so it is trusted. */ + case GRUB_ACPI_GENADDR_MEM_SPACE: + return grub_serial_ns8250_add_mmio (spcr->base_addr.addr, +- spcr->base_addr.access_size, &config); ++ spcr->base_addr.access_size, &config, ++ true); + case GRUB_ACPI_GENADDR_IO_SPACE: +- return grub_serial_ns8250_add_port (spcr->base_addr.addr, &config); ++ return grub_serial_ns8250_add_port (spcr->base_addr.addr, &config, true); + default: + return NULL; + }; +diff --git a/grub-core/term/ns8250.c b/grub-core/term/ns8250.c +index 23e8e09..014bb04 100644 +--- a/grub-core/term/ns8250.c ++++ b/grub-core/term/ns8250.c +@@ -347,7 +347,8 @@ grub_ns8250_hw_get_port (const unsigned int unit) + } + + struct grub_serial_port * +-grub_serial_ns8250_add_port (grub_port_t port, struct grub_serial_config *config) ++grub_serial_ns8250_add_port (grub_port_t port, struct grub_serial_config *config, ++ bool trusted) + { + struct grub_serial_port *p; + unsigned i; +@@ -363,14 +364,23 @@ grub_serial_ns8250_add_port (grub_port_t port, struct grub_serial_config *config + return &com_ports[i]; + } + ++ /* ++ * use_mmio and port share a union with the private data of every other back ++ * end, so they only mean anything on a port this driver registered. ++ */ + FOR_SERIAL_PORTS (p) +- if (p->use_mmio == false && p->port == port) ++ if (p->driver == &grub_ns8250_driver ++ && p->use_mmio == false && p->port == port) + { + if (config != NULL) + grub_serial_port_configure (p, config); + return p; + } + ++ /* No port is registered here, so going on means probing the address. */ ++ if (grub_serial_reject_untrusted_address (trusted) == true) ++ return NULL; ++ + grub_outb (0x5a, port + UART_SR); + if (grub_inb (port + UART_SR) != 0x5a) + return NULL; +@@ -402,7 +412,7 @@ grub_serial_ns8250_add_port (grub_port_t port, struct grub_serial_config *config + + struct grub_serial_port * + grub_serial_ns8250_add_mmio (grub_addr_t addr, unsigned int acc_size, +- struct grub_serial_config *config) ++ struct grub_serial_config *config, bool trusted) + { + struct grub_serial_port *p; + unsigned i; +@@ -415,14 +425,23 @@ grub_serial_ns8250_add_mmio (grub_addr_t addr, unsigned int acc_size, + return &com_ports[i]; + } + ++ /* ++ * use_mmio and mmio share a union with the private data of every other back ++ * end, so they only mean anything on a port this driver registered. ++ */ + FOR_SERIAL_PORTS (p) +- if (p->use_mmio == true && p->mmio.base == addr) ++ if (p->driver == &grub_ns8250_driver ++ && p->use_mmio == true && p->mmio.base == addr) + { + if (config != NULL) + grub_serial_port_configure (p, config); + return p; + } + ++ /* No port is registered here, so going on means programming the address. */ ++ if (grub_serial_reject_untrusted_address (trusted) == true) ++ return NULL; ++ + p = grub_malloc (sizeof (*p)); + if (p == NULL) + return NULL; +diff --git a/grub-core/term/serial.c b/grub-core/term/serial.c +index 8260dcb..57039dd 100644 +--- a/grub-core/term/serial.c ++++ b/grub-core/term/serial.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #ifdef GRUB_MACHINE_MIPS_LOONGSON + #include + #endif +@@ -140,6 +141,30 @@ static struct grub_term_output grub_serial_term_output = + + + ++/* ++ * An address that no port is registered at cannot be honored while lockdown ++ * is enforced: the probe and programming sequences in the ns8250 and pl011 ++ * back ends write to fixed offsets from it with no way to tell a UART from ++ * ordinary memory, so an address taken from an unauthenticated configuration ++ * is a write primitive against whatever it points at. ++ * ++ * A back end calls this once its own lookup has found no port already ++ * registered at the address, that is, at the point where it would otherwise ++ * touch the hardware. TRUSTED is true when the address came from a platform ++ * description -- grub_ns8250_init(), an ACPI SPCR table, PCI enumeration -- ++ * rather than from the caller. ++ */ ++bool ++grub_serial_reject_untrusted_address (bool trusted) ++{ ++ if (trusted == true || grub_is_lockdown () != GRUB_LOCKDOWN_ENABLED) ++ return false; ++ ++ grub_error (GRUB_ERR_ACCESS_DENIED, ++ N_("serial port address is not permitted when lockdown is enforced")); ++ return true; ++} ++ + struct grub_serial_port * + grub_serial_find (const char *name) + { +@@ -154,12 +179,18 @@ grub_serial_find (const char *name) + if (grub_strcmp (port->name, name) == 0) + return port; + ++ /* ++ * Everything below this point builds a port from an address the caller ++ * gave us, so the back ends are told the address is untrusted: under ++ * lockdown they hand back a port only if one is already registered at that ++ * address, which means the platform described that UART. ++ */ + #if (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) && !defined(GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC) + if (grub_strncmp (name, "port", sizeof ("port") - 1) == 0 + && grub_isxdigit (name [sizeof ("port") - 1])) + { + port = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1], +- 0, 16), NULL); ++ 0, 16), NULL, false); + if (port != NULL) + return port; + } +@@ -205,7 +236,7 @@ grub_serial_find (const char *name) + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("incorrect MMIO access size")); + } + +- port = grub_serial_ns8250_add_mmio (addr, acc_size, NULL); ++ port = grub_serial_ns8250_add_mmio (addr, acc_size, NULL, false); + if (port != NULL) + return port; + } +@@ -275,9 +306,18 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args) + + port = grub_serial_find (name); + if (!port) +- return grub_error (GRUB_ERR_BAD_ARGUMENT, +- N_("serial port `%s' isn't found"), +- name); ++ { ++ /* ++ * A back end that refused the port has already said why; keep that ++ * rather than reporting a misleading "isn't found". ++ */ ++ if (grub_errno == GRUB_ERR_ACCESS_DENIED) ++ return grub_errno; ++ ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("serial port `%s' isn't found"), ++ name); ++ } + + config = port->config; + +diff --git a/include/grub/serial.h b/include/grub/serial.h +index d7e0635..096209a 100644 +--- a/include/grub/serial.h ++++ b/include/grub/serial.h +@@ -188,14 +188,18 @@ grub_serial_config_defaults (struct grub_serial_port *port) + return port->driver->configure (port, &config); + } + ++bool grub_serial_reject_untrusted_address (bool trusted); ++ + #if defined(__mips__) || defined (__i386__) || defined (__x86_64__) + void grub_ns8250_init (void); + struct grub_serial_port *grub_ns8250_spcr_init (void); + struct grub_serial_port *grub_serial_ns8250_add_port (grub_port_t port, +- struct grub_serial_config *config); ++ struct grub_serial_config *config, ++ bool trusted); + struct grub_serial_port *grub_serial_ns8250_add_mmio (grub_addr_t addr, + unsigned int acc_size, +- struct grub_serial_config *config); ++ struct grub_serial_config *config, ++ bool trusted); + #endif + #ifdef GRUB_MACHINE_IEEE1275 + void grub_ofserial_init (void); diff -Nru grub2-2.12/debian/patches/series grub2-2.12/debian/patches/series --- grub2-2.12/debian/patches/series 2026-04-30 19:02:01.000000000 +0000 +++ grub2-2.12/debian/patches/series 2026-11-05 12:04:01.000000000 +0000 @@ -146,3 +146,13 @@ cve-2025-jan/fs-xfs-Fix-grub_xfs_iterate_dir-return-value-in-case-of-f.patch cve-2025-jan/fs-xfs-Propagate-incorrect-inode-error-from-grub_xfs_read.patch cve-2025-jan/fs-xfs-Handle-root-inode-read-failure-in-grub_xfs_mount.patch +cve-2025-nov/commands-test-Fix-error-in-recursion-depth-calculati.patch +cve-2025-nov/kern-file-Call-grub_dl_unref-after-fs-fs_close.patch +cve-2025-nov/net-net-Unregister-net_set_vlan-command-on-unload.patch +cve-2025-nov/gettext-gettext-Unregister-gettext-command-on-module.patch +cve-2025-nov/normal-main-Unregister-commands-on-module-unload.patch +cve-2025-nov/tests-lib-functional_test-Unregister-commands-on-mod.patch +cve-2025-nov/commands-usbtest-Use-correct-string-length-field.patch +cve-2025-nov/commands-usbtest-Ensure-string-length-is-sufficient-.patch +cve-2026-sep/docs-grub-Document-serial-port-restrictions-under-lockdow.patch +cve-2026-sep/term-serial-Reject-caller-supplied-port-addresses-under-l.patch diff -Nru grub2-2.12/debian/sbat.debian.csv.in grub2-2.12/debian/sbat.debian.csv.in --- grub2-2.12/debian/sbat.debian.csv.in 2025-07-03 16:35:51.000000000 +0000 +++ grub2-2.12/debian/sbat.debian.csv.in 2026-11-05 12:04:01.000000000 +0000 @@ -1,5 +1,5 @@ sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md -grub,5,Free Software Foundation,grub,@UPSTREAM_VERSION@,https://www.gnu.org/software/grub/ +grub,7,Free Software Foundation,grub,@UPSTREAM_VERSION@,https://www.gnu.org/software/grub/ grub.debian,5,Debian,grub2,@DEB_VERSION@,https://tracker.debian.org/pkg/grub2 grub.debian13,1,Debian,grub2,@DEB_VERSION@,https://tracker.debian.org/pkg/grub2 grub.peimage,2,Canonical,grub2,@DEB_VERSION@,https://salsa.debian.org/grub-team/grub/-/blob/master/debian/patches/secure-boot/efi-use-peimage-shim.patch