@samitouri / QOSamiQemu / commits / a3856a7ba4

pc-bios/s390-ccw: bound zipl menu strlen and replace VLA in zipl_print_entry

menu_get_zipl_boot_index() calls strlen() on a pointer into the middle of _s2 with no upper bound, so a stage-2 image whose blocks contain no NUL bytes causes strlen() to walk beyond _s2. The resulting length is then used to size a stack VLA in zipl_print_entry(), risking a stack overflow. Fix by: - Implementing strnlen(), a bounded version of strlen(). s390-ccw uses libc from SLOF, which includes strlen() but does not have an implementation of strnlen(), so we must implement our own. - Adding a menu_data_end parameter to menu_get_zipl_boot_index() and replacing both strlen() calls with strnlen() bounded by the remaining buffer space. The loop guard also checks that the pointer has not reached menu_data_end. The function returns 0 (boot default) if somehow menu_data reaches menu_data_end before printing any entries. - Replacing the VLA char buf[len + 2] in zipl_print_entry() with a fixed ZIPL_ENTRY_MAX + 2 (82-byte) buffer and truncating len before use. - Passing s2_end (_s2 + sizeof(_s2)) as menu_data_end at the one call site in eckd_get_boot_menu_index(), so the bound is exactly the end of the buffer. Fixes: f7178910845a ("s390-ccw: print zipl boot menu") Cc: qemu-stable@nongnu.org Signed-off-by: Joshua Daley <jdaley@linux.ibm.com> Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com> Link: https://lore.kernel.org/qemu-devel/20260728134704.2924005-2-jdaley@linux.ibm.com [farman@linux.ibm.com: Per list, add strnlen rationale to commit message and added cc stable] Signed-off-by: Eric Farman <farman@linux.ibm.com>

Joshua Daley committed Jul 28, 2026 at 15:47 UTC a3856a7ba44ba1b740eee7759b03079e2f5f637b
4 files changed +37 -8
pc-bios/s390-ccw/bootmap.c
+3 -1
@@ -61,6 +61,7 @@ static uint8_t _s2[MAX_SECTOR_SIZE * 3] __attribute__((__aligned__(PAGE_SIZE)));
61 static void *s2_prev_blk = _s2;
62 static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE;
63 static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2;
64 +static void *s2_end = _s2 + sizeof(_s2);
65
66 static inline int verify_boot_info(BootInfo *bip)
67 {
@@ -308,7 +309,8 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr)
309 }
310 }
311
311 - return menu_get_zipl_boot_index(s2_cur_blk + banner_offset);
312 + return menu_get_zipl_boot_index(s2_cur_blk + banner_offset,
313 + s2_end);
314 }
315
316 prev_block_nr = cur_block_nr;
pc-bios/s390-ccw/helper.h
+10
@@ -45,4 +45,14 @@ static inline void sleep(unsigned int seconds)
45 }
46 }
47
48 +static inline size_t strnlen(const char *s, size_t maxlen)
49 +{
50 + size_t len = 0;
51 +
52 + while (len < maxlen && s[len]) {
53 + len++;
54 + }
55 + return len;
56 +}
57 +
58 #endif
pc-bios/s390-ccw/menu.c
+23 -6
@@ -16,6 +16,7 @@
16 #include "s390-ccw.h"
17 #include "sclp.h"
18 #include "s390-time.h"
19 +#include "helper.h"
20
21 #define KEYCODE_NO_INP '\0'
22 #define KEYCODE_ESCAPE '\033'
@@ -26,6 +27,9 @@
27 #define ZIPL_TIMEOUT_OFFSET 138
28 #define ZIPL_FLAG_OFFSET 140
29
30 +/* Max printable chars for a zipl boot menu entry */
31 +#define ZIPL_ENTRY_MAX 80
32 +
33 #define TOD_CLOCK_MILLISECOND 0x3e8000
34
35 #define LOW_CORE_EXTERNAL_INT_ADDR 0x86
@@ -179,9 +183,13 @@ int menu_get_boot_index(bool *valid_entries)
183 /* Returns the entry number that was printed, or -1 on invalid entry */
184 static int zipl_print_entry(const char *data, size_t len)
185 {
182 - char buf[len + 2];
186 + char buf[ZIPL_ENTRY_MAX + 2];
187 const char *p;
188
189 + if (len > ZIPL_ENTRY_MAX) {
190 + len = ZIPL_ENTRY_MAX;
191 + }
192 +
193 ebcdic_to_ascii(data, buf, len);
194 buf[len] = '\n';
195 buf[len + 1] = '\0';
@@ -196,7 +204,7 @@ static int zipl_print_entry(const char *data, size_t len)
204 return atoi(p);
205 }
206
199 -int menu_get_zipl_boot_index(const char *menu_data)
207 +int menu_get_zipl_boot_index(const char *menu_data, const char *menu_data_end)
208 {
209 size_t len;
210 int entry;
@@ -212,13 +220,22 @@ int menu_get_zipl_boot_index(const char *menu_data)
220 timeout = zipl_timeout * 1000;
221 }
222
215 - /* Print banner */
223 + if (menu_data >= menu_data_end) {
224 + return 0; /* Boot default */
225 + }
226 +
227 + /* Skip banner */
228 + len = strnlen(menu_data, menu_data_end - menu_data);
229 + menu_data += len + 1;
230 + if (menu_data >= menu_data_end || !(*menu_data)) {
231 + return 0; /* No entries, boot default */
232 + }
233 +
234 puts("s390-ccw zIPL Boot Menu\n");
217 - menu_data += strlen(menu_data) + 1;
235
236 /* Print entries */
220 - while (*menu_data) {
221 - len = strlen(menu_data);
237 + while (menu_data < menu_data_end && *menu_data) {
238 + len = strnlen(menu_data, menu_data_end - menu_data);
239 entry = zipl_print_entry(menu_data, len);
240 menu_data += len + 1;
241
pc-bios/s390-ccw/s390-ccw.h
+1 -1
@@ -76,7 +76,7 @@ void jump_to_low_kernel(void);
76
77 /* menu.c */
78 void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout);
79 -int menu_get_zipl_boot_index(const char *menu_data);
79 +int menu_get_zipl_boot_index(const char *menu_data, const char *menu_data_end);
80 bool menu_is_enabled_zipl(void);
81 int menu_get_enum_boot_index(bool *valid_entries);
82 bool menu_is_enabled_enum(void);