From: Joshua Daley <[email protected]>
menu_get_zipl_boot_index() iterates NUL-separated strings from the
zipl stage-2 boot-menu block, passes each to zipl_print_entry() which
converts EBCDIC to ASCII and returns atoi(), then writes true into
valid_entries[entry]. valid_entries is a MAX_BOOT_ENTRIES element
stack array, but entry was never bounds-checked, so a crafted on-disk
value could index arbitrarily beyond the array.
Fix this in two places:
- zipl_print_entry() now validates that the first significant character
(after an optional leading space) is a digit. Entries that fail this
check return -1 without printing.
- menu_get_zipl_boot_index() skips any entry whose index is outside
[0, MAX_BOOT_ENTRIES) before writing to valid_entries[].
Fixes: 7385e947fc65 ("pc-bios/s390-ccw: fix non-sequential boot entries (eckd)")
Signed-off-by: Joshua Daley <[email protected]>
---
pc-bios/s390-ccw/menu.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c
index eeaff78f87..b6a9a56d46 100644
--- a/pc-bios/s390-ccw/menu.c
+++ b/pc-bios/s390-ccw/menu.c
@@ -176,18 +176,24 @@ int menu_get_boot_index(bool *valid_entries)
return boot_index;
}
-/* Returns the entry number that was printed */
+/* Returns the entry number that was printed, or -1 on invalid entry */
static int zipl_print_entry(const char *data, size_t len)
{
char buf[len + 2];
+ const char *p;
ebcdic_to_ascii(data, buf, len);
buf[len] = '\n';
buf[len + 1] = '\0';
+ p = (buf[0] == ' ') ? buf + 1 : buf;
+ if (!isdigit((unsigned char)*p)) {
+ return -1;
+ }
+
printf("%s", buf);
- return buf[0] == ' ' ? atoi(buf + 1) : atoi(buf);
+ return atoi(p);
}
int menu_get_zipl_boot_index(const char *menu_data)
@@ -216,6 +222,9 @@ int menu_get_zipl_boot_index(const char *menu_data)
entry = zipl_print_entry(menu_data, len);
menu_data += len + 1;
+ if (entry < 0 || entry >= MAX_BOOT_ENTRIES) {
+ continue;
+ }
valid_entries[entry] = true;
if (entry == 0) {
--
2.53.0