* [PATCH] x86/bzimage: Check that the payload lies within the image @ 2026-10-20 21:48 Stefano Stabellini 2026-10-21 9:30 ` Roger Pau Monné 2026-10-26 22:10 ` [PATCH v2] " Stefano Stabellini 0 siblings, 2 replies; 7+ messages in thread From: Stefano Stabellini @ 2026-10-20 21:48 UTC (permalink / raw) To: xen-devel Cc: jbeulich, andrew.cooper3, roger, jason.andryuk, teddy.astie, stefano.stabellini bzimage_check() doesn't validate setup_sects, payload_offset or payload_length against the image size. A truncated or malformed bzImage therefore makes bzimage_headroom() and bzimage_parse() read beyond the end of the boot module, which shows up as a fatal page fault, a bogus headroom, or a decompression failure, rather than as a bad image. Reject such a header up front. bzimage_parse() passes bzimage_check() a length which includes the decompression headroom in front of the image, so subtract it to check against the image alone. Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com> --- xen/arch/x86/bzimage.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/xen/arch/x86/bzimage.c b/xen/arch/x86/bzimage.c index 66f648f311..a5c48755b6 100644 --- a/xen/arch/x86/bzimage.c +++ b/xen/arch/x86/bzimage.c @@ -54,6 +54,8 @@ struct __packed setup_header { static __init int bzimage_check(struct setup_header *hdr, unsigned long len) { + unsigned long payload_start; + if ( len < sizeof(struct setup_header) ) return 0; @@ -65,6 +67,15 @@ static __init int bzimage_check(struct setup_header *hdr, unsigned long len) hdr->version >> 8, hdr->version & 0xff); return -EINVAL; } + + payload_start = (hdr->setup_sects + 1) * 512UL + hdr->payload_offset; + if ( payload_start > len || hdr->payload_length > len - payload_start ) + { + printk("bzImage payload %#x@%#lx exceeds the %#lx byte image\n", + hdr->payload_length, payload_start, len); + return -EINVAL; + } + return 1; } @@ -107,7 +118,8 @@ int __init bzimage_parse(void *image_base, void **image_start, unsigned long *image_len) { struct setup_header *hdr = (struct setup_header *)(*image_start); - int err = bzimage_check(hdr, *image_len); + /* *image_len also counts the headroom ahead of *image_start. */ + int err = bzimage_check(hdr, *image_len - (*image_start - image_base)); unsigned long output_len; if ( err < 0 ) -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] x86/bzimage: Check that the payload lies within the image 2026-10-20 21:48 [PATCH] x86/bzimage: Check that the payload lies within the image Stefano Stabellini @ 2026-10-21 9:30 ` Roger Pau Monné 2026-10-26 12:23 ` Jan Beulich 2026-10-26 22:10 ` [PATCH v2] " Stefano Stabellini 1 sibling, 1 reply; 7+ messages in thread From: Roger Pau Monné @ 2026-10-21 9:30 UTC (permalink / raw) To: Stefano Stabellini Cc: xen-devel, jbeulich, andrew.cooper3, jason.andryuk, teddy.astie On Tue, Oct 20, 2026 at 02:48:25PM -0700, Stefano Stabellini wrote: > bzimage_check() doesn't validate setup_sects, payload_offset or > payload_length against the image size. A truncated or malformed bzImage > therefore makes bzimage_headroom() and bzimage_parse() read beyond the > end of the boot module, which shows up as a fatal page fault, a bogus > headroom, or a decompression failure, rather than as a bad image. > > Reject such a header up front. bzimage_parse() passes bzimage_check() a > length which includes the decompression headroom in front of the image, > so subtract it to check against the image alone. > > Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com> > --- > xen/arch/x86/bzimage.c | 14 +++++++++++++- > 1 file changed, 13 insertions(+), 1 deletion(-) > > diff --git a/xen/arch/x86/bzimage.c b/xen/arch/x86/bzimage.c > index 66f648f311..a5c48755b6 100644 > --- a/xen/arch/x86/bzimage.c > +++ b/xen/arch/x86/bzimage.c > @@ -54,6 +54,8 @@ struct __packed setup_header { > > static __init int bzimage_check(struct setup_header *hdr, unsigned long len) > { > + unsigned long payload_start; > + > if ( len < sizeof(struct setup_header) ) > return 0; > > @@ -65,6 +67,15 @@ static __init int bzimage_check(struct setup_header *hdr, unsigned long len) > hdr->version >> 8, hdr->version & 0xff); > return -EINVAL; > } > + > + payload_start = (hdr->setup_sects + 1) * 512UL + hdr->payload_offset; Do you really need the UL, doesn't it get auto-promoted to unsigned long already? If there's some issue with the types, that requires the calculation to be promoted top unsigned long, the other instances of this calculation should also be adjusted. > + if ( payload_start > len || hdr->payload_length > len - payload_start ) > + { > + printk("bzImage payload %#x@%#lx exceeds the %#lx byte image\n", Maybe best wqorded as: "... payload XXX past the image size (%#lx)" I think the "byte image" wording is not very clear IMO. > + hdr->payload_length, payload_start, len); > + return -EINVAL; > + } > + > return 1; > } > > @@ -107,7 +118,8 @@ int __init bzimage_parse(void *image_base, void **image_start, > unsigned long *image_len) > { > struct setup_header *hdr = (struct setup_header *)(*image_start); > - int err = bzimage_check(hdr, *image_len); > + /* *image_len also counts the headroom ahead of *image_start. */ IMO you can remove the dereferences from the variable names, it just makes the comment harder to reads for no real benefit, readers already know by context you are referencing the contents, not the pointer address. Thanks, Roger. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] x86/bzimage: Check that the payload lies within the image 2026-10-21 9:30 ` Roger Pau Monné @ 2026-10-26 12:23 ` Jan Beulich 0 siblings, 0 replies; 7+ messages in thread From: Jan Beulich @ 2026-10-26 12:23 UTC (permalink / raw) To: Roger Pau Monné, Stefano Stabellini Cc: xen-devel, andrew.cooper3, jason.andryuk, teddy.astie On 21.10.2026 11:30, Roger Pau Monné wrote: > On Tue, Oct 20, 2026 at 02:48:25PM -0700, Stefano Stabellini wrote: >> @@ -65,6 +67,15 @@ static __init int bzimage_check(struct setup_header *hdr, unsigned long len) >> hdr->version >> 8, hdr->version & 0xff); >> return -EINVAL; >> } >> + >> + payload_start = (hdr->setup_sects + 1) * 512UL + hdr->payload_offset; > > Do you really need the UL, doesn't it get auto-promoted to unsigned long > already? Without taking the type of .setup_sects into account, the suffix may appear necessary. But yes, the field being a 8-bit one, the multiplication can't overflow when done in 32 bits, so no suffix is needed (and generated code may - at least in principle - be slightly better without the suffix). Jan ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2] x86/bzimage: Check that the payload lies within the image 2026-10-20 21:48 [PATCH] x86/bzimage: Check that the payload lies within the image Stefano Stabellini 2026-10-21 9:30 ` Roger Pau Monné @ 2026-10-26 22:10 ` Stefano Stabellini 2026-10-27 10:51 ` Jan Beulich 1 sibling, 1 reply; 7+ messages in thread From: Stefano Stabellini @ 2026-10-26 22:10 UTC (permalink / raw) To: xen-devel Cc: Stefano Stabellini, jbeulich, andrew.cooper3, roger, jason.andryuk, teddy.astie bzimage_check() doesn't validate setup_sects, payload_offset or payload_length against the image size. A truncated or malformed bzImage therefore makes bzimage_headroom() and bzimage_parse() read beyond the end of the boot module, which shows up as a fatal page fault, a bogus headroom, or a decompression failure, rather than as a bad image. Reject such a header up front. bzimage_parse() passes bzimage_check() a length which includes the decompression headroom in front of the image, so subtract it to check against the image alone. Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com> --- Changes in v2: - drop the UL suffix: setup_sects is 8 bits wide, so the multiplication cannot overflow, and the offset is now computed the same way as where bzimage_headroom() and bzimage_parse() use it - reword the error message - drop the dereferences from the comment xen/arch/x86/bzimage.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/xen/arch/x86/bzimage.c b/xen/arch/x86/bzimage.c index 66f648f311..41f19221ac 100644 --- a/xen/arch/x86/bzimage.c +++ b/xen/arch/x86/bzimage.c @@ -54,6 +54,8 @@ struct __packed setup_header { static __init int bzimage_check(struct setup_header *hdr, unsigned long len) { + unsigned long payload_start; + if ( len < sizeof(struct setup_header) ) return 0; @@ -65,6 +67,15 @@ static __init int bzimage_check(struct setup_header *hdr, unsigned long len) hdr->version >> 8, hdr->version & 0xff); return -EINVAL; } + + payload_start = (hdr->setup_sects + 1) * 512 + hdr->payload_offset; + if ( payload_start > len || hdr->payload_length > len - payload_start ) + { + printk("bzImage payload %#x@%#lx past the image size (%#lx)\n", + hdr->payload_length, payload_start, len); + return -EINVAL; + } + return 1; } @@ -107,7 +118,8 @@ int __init bzimage_parse(void *image_base, void **image_start, unsigned long *image_len) { struct setup_header *hdr = (struct setup_header *)(*image_start); - int err = bzimage_check(hdr, *image_len); + /* image_len also counts the headroom ahead of image_start. */ + int err = bzimage_check(hdr, *image_len - (*image_start - image_base)); unsigned long output_len; if ( err < 0 ) -- 2.47.3 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2] x86/bzimage: Check that the payload lies within the image 2026-10-26 22:10 ` [PATCH v2] " Stefano Stabellini @ 2026-10-27 10:51 ` Jan Beulich 2026-10-27 19:43 ` Stefano Stabellini 0 siblings, 1 reply; 7+ messages in thread From: Jan Beulich @ 2026-10-27 10:51 UTC (permalink / raw) To: Stefano Stabellini Cc: andrew.cooper3, roger, jason.andryuk, teddy.astie, xen-devel On 26.10.2026 23:10, Stefano Stabellini wrote: > bzimage_check() doesn't validate setup_sects, payload_offset or > payload_length against the image size. A truncated or malformed bzImage > therefore makes bzimage_headroom() and bzimage_parse() read beyond the > end of the boot module, which shows up as a fatal page fault, a bogus > headroom, or a decompression failure, rather than as a bad image. > > Reject such a header up front. bzimage_parse() passes bzimage_check() a > length which includes the decompression headroom in front of the image, > so subtract it to check against the image alone. > > Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com> > --- > Changes in v2: > - drop the UL suffix: setup_sects is 8 bits wide, so the multiplication > cannot overflow, and the offset is now computed the same way as where > bzimage_headroom() and bzimage_parse() use it Upon looking again, I think this may have been a mistake: > @@ -65,6 +67,15 @@ static __init int bzimage_check(struct setup_header *hdr, unsigned long len) > hdr->version >> 8, hdr->version & 0xff); > return -EINVAL; > } > + > + payload_start = (hdr->setup_sects + 1) * 512 + hdr->payload_offset; The multiplication can't overflow, but the addition can. Question is what the behavior should be in that case: Adding back the UL suffix would allow payload_start to go beyond 4Gb, but that may not be what is intended. The alternative would be to check for overflow in the calculation (easiest by way of using __builtin_add_overflow(), yet then needing adjustment when backporting to 4.20). > + if ( payload_start > len || hdr->payload_length > len - payload_start ) > + { > + printk("bzImage payload %#x@%#lx past the image size (%#lx)\n", As we prefer messages to be terse, may I suggest to also drop "the"? Jan ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2] x86/bzimage: Check that the payload lies within the image 2026-10-27 10:51 ` Jan Beulich @ 2026-10-27 19:43 ` Stefano Stabellini 2026-10-28 7:05 ` Jan Beulich 0 siblings, 1 reply; 7+ messages in thread From: Stefano Stabellini @ 2026-10-27 19:43 UTC (permalink / raw) To: Jan Beulich Cc: Stefano Stabellini, andrew.cooper3, roger, jason.andryuk, teddy.astie, xen-devel On Tue, 27 Oct 2026, Jan Beulich wrote: > On 26.10.2026 23:10, Stefano Stabellini wrote: > > bzimage_check() doesn't validate setup_sects, payload_offset or > > payload_length against the image size. A truncated or malformed bzImage > > therefore makes bzimage_headroom() and bzimage_parse() read beyond the > > end of the boot module, which shows up as a fatal page fault, a bogus > > headroom, or a decompression failure, rather than as a bad image. > > > > Reject such a header up front. bzimage_parse() passes bzimage_check() a > > length which includes the decompression headroom in front of the image, > > so subtract it to check against the image alone. > > > > Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com> > > --- > > Changes in v2: > > - drop the UL suffix: setup_sects is 8 bits wide, so the multiplication > > cannot overflow, and the offset is now computed the same way as where > > bzimage_headroom() and bzimage_parse() use it > > Upon looking again, I think this may have been a mistake: > > > @@ -65,6 +67,15 @@ static __init int bzimage_check(struct setup_header *hdr, unsigned long len) > > hdr->version >> 8, hdr->version & 0xff); > > return -EINVAL; > > } > > + > > + payload_start = (hdr->setup_sects + 1) * 512 + hdr->payload_offset; > > The multiplication can't overflow, but the addition can. Question is what > the behavior should be in that case: Adding back the UL suffix would allow > payload_start to go beyond 4Gb, but that may not be what is intended. The > alternative would be to check for overflow in the calculation (easiest by > way of using __builtin_add_overflow(), yet then needing adjustment when > backporting to 4.20). What about this From: Stefano Stabellini <stefano.stabellini@amd.com> Subject: [PATCH v3] x86/bzimage: Check that the payload lies within the image bzimage_check() doesn't validate setup_sects, payload_offset or payload_length against the image size. A truncated or malformed bzImage therefore makes bzimage_headroom() and bzimage_parse() read beyond the end of the boot module, which shows up as a fatal page fault, a bogus headroom, or a decompression failure, rather than as a bad image. Reject such a header up front, including one whose payload offset overflows the 32-bit calculation bzimage_headroom() and bzimage_parse() use. bzimage_parse() passes bzimage_check() a length which includes the decompression headroom in front of the image, so subtract it to check against the image alone. Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com> --- Changes in v3: - reject a payload offset whose 32-bit calculation wraps; the addend is at most 256 * 512, so a wrapped sum is smaller than payload_offset - drop "the" from the error message Changes in v2: - drop the UL suffix: setup_sects is 8 bits wide, so the multiplication cannot overflow, and the offset is now computed the same way as where bzimage_headroom() and bzimage_parse() use it - reword the error message - drop the dereferences from the comment --- xen/arch/x86/bzimage.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/xen/arch/x86/bzimage.c b/xen/arch/x86/bzimage.c index 66f648f311..a0cba65000 100644 --- a/xen/arch/x86/bzimage.c +++ b/xen/arch/x86/bzimage.c @@ -54,6 +54,8 @@ struct __packed setup_header { static __init int bzimage_check(struct setup_header *hdr, unsigned long len) { + unsigned int payload_start; + if ( len < sizeof(struct setup_header) ) return 0; @@ -65,6 +67,16 @@ static __init int bzimage_check(struct setup_header *hdr, unsigned long len) hdr->version >> 8, hdr->version & 0xff); return -EINVAL; } + + payload_start = (hdr->setup_sects + 1) * 512 + hdr->payload_offset; + if ( payload_start < hdr->payload_offset || + payload_start > len || hdr->payload_length > len - payload_start ) + { + printk("bzImage payload %#x@%#x past image size (%#lx)\n", + hdr->payload_length, payload_start, len); + return -EINVAL; + } + return 1; } @@ -107,7 +119,8 @@ int __init bzimage_parse(void *image_base, void **image_start, unsigned long *image_len) { struct setup_header *hdr = (struct setup_header *)(*image_start); - int err = bzimage_check(hdr, *image_len); + /* image_len also counts the headroom ahead of image_start. */ + int err = bzimage_check(hdr, *image_len - (*image_start - image_base)); unsigned long output_len; if ( err < 0 ) -- 2.47.3 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2] x86/bzimage: Check that the payload lies within the image 2026-10-27 19:43 ` Stefano Stabellini @ 2026-10-28 7:05 ` Jan Beulich 0 siblings, 0 replies; 7+ messages in thread From: Jan Beulich @ 2026-10-28 7:05 UTC (permalink / raw) To: Stefano Stabellini Cc: andrew.cooper3, roger, jason.andryuk, teddy.astie, xen-devel On 27.10.2026 20:43, Stefano Stabellini wrote: > On Tue, 27 Oct 2026, Jan Beulich wrote: >> On 26.10.2026 23:10, Stefano Stabellini wrote: >>> bzimage_check() doesn't validate setup_sects, payload_offset or >>> payload_length against the image size. A truncated or malformed bzImage >>> therefore makes bzimage_headroom() and bzimage_parse() read beyond the >>> end of the boot module, which shows up as a fatal page fault, a bogus >>> headroom, or a decompression failure, rather than as a bad image. >>> >>> Reject such a header up front. bzimage_parse() passes bzimage_check() a >>> length which includes the decompression headroom in front of the image, >>> so subtract it to check against the image alone. >>> >>> Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com> >>> --- >>> Changes in v2: >>> - drop the UL suffix: setup_sects is 8 bits wide, so the multiplication >>> cannot overflow, and the offset is now computed the same way as where >>> bzimage_headroom() and bzimage_parse() use it >> >> Upon looking again, I think this may have been a mistake: >> >>> @@ -65,6 +67,15 @@ static __init int bzimage_check(struct setup_header *hdr, unsigned long len) >>> hdr->version >> 8, hdr->version & 0xff); >>> return -EINVAL; >>> } >>> + >>> + payload_start = (hdr->setup_sects + 1) * 512 + hdr->payload_offset; >> >> The multiplication can't overflow, but the addition can. Question is what >> the behavior should be in that case: Adding back the UL suffix would allow >> payload_start to go beyond 4Gb, but that may not be what is intended. The >> alternative would be to check for overflow in the calculation (easiest by >> way of using __builtin_add_overflow(), yet then needing adjustment when >> backporting to 4.20). > > What about this > > > From: Stefano Stabellini <stefano.stabellini@amd.com> > Subject: [PATCH v3] x86/bzimage: Check that the payload lies within the image > > bzimage_check() doesn't validate setup_sects, payload_offset or > payload_length against the image size. A truncated or malformed bzImage > therefore makes bzimage_headroom() and bzimage_parse() read beyond the > end of the boot module, which shows up as a fatal page fault, a bogus > headroom, or a decompression failure, rather than as a bad image. > > Reject such a header up front, including one whose payload offset > overflows the 32-bit calculation bzimage_headroom() and bzimage_parse() > use. bzimage_parse() passes bzimage_check() a length which includes the > decompression headroom in front of the image, so subtract it to check > against the image alone. > > Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com> Reviewed-by: Jan Beulich <jbeulich@suse.com> ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-28 7:06 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-20 21:48 [PATCH] x86/bzimage: Check that the payload lies within the image Stefano Stabellini 2026-10-21 9:30 ` Roger Pau Monné 2026-10-26 12:23 ` Jan Beulich 2026-10-26 22:10 ` [PATCH v2] " Stefano Stabellini 2026-10-27 10:51 ` Jan Beulich 2026-10-27 19:43 ` Stefano Stabellini 2026-10-28 7:05 ` Jan Beulich
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.