The comment highlights just how bogus this really is. Being an initcall, the
boot allocator is long gone, and bootstrap_unmap() is a no-op.
The fact there is nothing to do should be a giant red flag about the validity
of the mappings "being freed". Indeed, they both constitute use-after-frees.
We could move the size/data/end clobbering into microcode_init_cache() which
is the final consumer of the information, but we're intending to delete these
static variables entirely, and it's less churn to just leave them dangling for
a few patches.
No functional change.
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Jan Beulich <JBeulich@suse.com>
CC: Roger Pau Monné <roger.pau@citrix.com>
CC: Daniel P. Smith <dpsmith@apertussolutions.com>
After the $N'th rearranging, this could actually be merged into "x86/ucode:
Drop ucode_mod and ucode_blob" with no effect on the intermediate patches.
---
xen/arch/x86/cpu/microcode/core.c | 22 ----------------------
1 file changed, 22 deletions(-)
diff --git a/xen/arch/x86/cpu/microcode/core.c b/xen/arch/x86/cpu/microcode/core.c
index 9a2cc631d2aa..3e815f1880af 100644
--- a/xen/arch/x86/cpu/microcode/core.c
+++ b/xen/arch/x86/cpu/microcode/core.c
@@ -758,28 +758,6 @@ int microcode_update(XEN_GUEST_HANDLE(const_void) buf,
return continue_hypercall_on_cpu(0, microcode_update_helper, buffer);
}
-static int __init cf_check microcode_init(void)
-{
- /*
- * At this point, all CPUs should have updated their microcode
- * via the early_microcode_* paths so free the microcode blob.
- */
- if ( ucode_blob.size )
- {
- bootstrap_unmap();
- ucode_blob.size = 0;
- ucode_blob.data = NULL;
- }
- else if ( ucode_mod.mod_end )
- {
- bootstrap_unmap();
- ucode_mod.mod_end = 0;
- }
-
- return 0;
-}
-__initcall(microcode_init);
-
/* Load a cached update to current cpu */
int microcode_update_one(void)
{
--
2.39.5
On 10/28/24 05:18, Andrew Cooper wrote: > The comment highlights just how bogus this really is. Being an initcall, the > boot allocator is long gone, and bootstrap_unmap() is a no-op. > > The fact there is nothing to do should be a giant red flag about the validity > of the mappings "being freed". Indeed, they both constitute use-after-frees. > > We could move the size/data/end clobbering into microcode_init_cache() which > is the final consumer of the information, but we're intending to delete these > static variables entirely, and it's less churn to just leave them dangling for > a few patches. > > No functional change. > > Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> > --- > CC: Jan Beulich <JBeulich@suse.com> > CC: Roger Pau Monné <roger.pau@citrix.com> > CC: Daniel P. Smith <dpsmith@apertussolutions.com> > > After the $N'th rearranging, this could actually be merged into "x86/ucode: > Drop ucode_mod and ucode_blob" with no effect on the intermediate patches. > --- Not sure if this helps, but for clarification sake, the module itself is freed by discard_initial_images() and ucode_mod is __initdata and will be cleaned up with the rest of _initdata. This function is called after the last user of ucode_mod and the call to bootstrap_unmap() perpetuates the assumption that some how the underlying module has remained mapped. There is not any condition where this function does anything that would affect the execution of the hypervisor. It frees nothing and sets the value of a unit local variable that is no longer used shortly before the backing memory is freed. As to patch ordering, I have no opinion. Reviewed-by: Daniel P. Smith <dpsmith@apertussolutions.com>
On 28.10.2024 10:18, Andrew Cooper wrote: > The comment highlights just how bogus this really is. Being an initcall, the > boot allocator is long gone, and bootstrap_unmap() is a no-op. How's the boot allocator coming into the picture here? This is all about (un)mapping, not allocating. > The fact there is nothing to do should be a giant red flag about the validity > of the mappings "being freed". Indeed, they both constitute use-after-frees. I can't spot any use-after-free; the pointers in question ... > --- a/xen/arch/x86/cpu/microcode/core.c > +++ b/xen/arch/x86/cpu/microcode/core.c > @@ -758,28 +758,6 @@ int microcode_update(XEN_GUEST_HANDLE(const_void) buf, > return continue_hypercall_on_cpu(0, microcode_update_helper, buffer); > } > > -static int __init cf_check microcode_init(void) > -{ > - /* > - * At this point, all CPUs should have updated their microcode > - * via the early_microcode_* paths so free the microcode blob. > - */ > - if ( ucode_blob.size ) > - { > - bootstrap_unmap(); > - ucode_blob.size = 0; > - ucode_blob.data = NULL; > - } > - else if ( ucode_mod.mod_end ) > - { > - bootstrap_unmap(); > - ucode_mod.mod_end = 0; > - } > - > - return 0; > -} > -__initcall(microcode_init); ... aren't used anywhere. bootstrap_unmap() is "just in case" (perhaps indeed a no-op at least nowadays), and the rest is field clobbering. I'm okay with the code change, so Acked-by: Jan Beulich <jbeulich@suse.com> yet I'd like to ask for the description to be "softened" some. Jan
On 28/10/2024 1:38 pm, Jan Beulich wrote: > On 28.10.2024 10:18, Andrew Cooper wrote: >> The comment highlights just how bogus this really is. Being an initcall, the >> boot allocator is long gone, and bootstrap_unmap() is a no-op. > How's the boot allocator coming into the picture here? This is all about > (un)mapping, not allocating. > >> The fact there is nothing to do should be a giant red flag about the validity >> of the mappings "being freed". Indeed, they both constitute use-after-frees. > I can't spot any use-after-free; the pointers in question ... > >> --- a/xen/arch/x86/cpu/microcode/core.c >> +++ b/xen/arch/x86/cpu/microcode/core.c >> @@ -758,28 +758,6 @@ int microcode_update(XEN_GUEST_HANDLE(const_void) buf, >> return continue_hypercall_on_cpu(0, microcode_update_helper, buffer); >> } >> >> -static int __init cf_check microcode_init(void) >> -{ >> - /* >> - * At this point, all CPUs should have updated their microcode >> - * via the early_microcode_* paths so free the microcode blob. >> - */ >> - if ( ucode_blob.size ) >> - { >> - bootstrap_unmap(); >> - ucode_blob.size = 0; >> - ucode_blob.data = NULL; >> - } >> - else if ( ucode_mod.mod_end ) >> - { >> - bootstrap_unmap(); >> - ucode_mod.mod_end = 0; >> - } >> - >> - return 0; >> -} >> -__initcall(microcode_init); > ... aren't used anywhere. bootstrap_unmap() is "just in case" (perhaps indeed > a no-op at least nowadays), and the rest is field clobbering. I'm okay with the > code change, so > Acked-by: Jan Beulich <jbeulich@suse.com> > yet I'd like to ask for the description to be "softened" some. As I said, this could be folded into patch 9, given this particular arrangement of the series. The UAFs are apparent *because* the comment demonstrates a false line of reasoning. ucode_mod literally is used after free. ucode=$n is genuinely buggy today, because its a stash of a physical pointer across move_xen(). ucode_blob stashes a virtual pointer. This was even noticed in dc380df12acf ("x86/ucode: load microcode earlier on boot CPU") --- It needs to rescan the modules in order to find the new virtual address of the ucode blob because it changes during the boot process, e.g. from 0x00000000010802fc to 0xffff83204dac52fc. --- which highlighted the problem but duct-taped over it. ~Andrew
On 28.10.2024 18:12, Andrew Cooper wrote: > On 28/10/2024 1:38 pm, Jan Beulich wrote: >> On 28.10.2024 10:18, Andrew Cooper wrote: >>> The comment highlights just how bogus this really is. Being an initcall, the >>> boot allocator is long gone, and bootstrap_unmap() is a no-op. >> How's the boot allocator coming into the picture here? This is all about >> (un)mapping, not allocating. >> >>> The fact there is nothing to do should be a giant red flag about the validity >>> of the mappings "being freed". Indeed, they both constitute use-after-frees. >> I can't spot any use-after-free; the pointers in question ... >> >>> --- a/xen/arch/x86/cpu/microcode/core.c >>> +++ b/xen/arch/x86/cpu/microcode/core.c >>> @@ -758,28 +758,6 @@ int microcode_update(XEN_GUEST_HANDLE(const_void) buf, >>> return continue_hypercall_on_cpu(0, microcode_update_helper, buffer); >>> } >>> >>> -static int __init cf_check microcode_init(void) >>> -{ >>> - /* >>> - * At this point, all CPUs should have updated their microcode >>> - * via the early_microcode_* paths so free the microcode blob. >>> - */ >>> - if ( ucode_blob.size ) >>> - { >>> - bootstrap_unmap(); >>> - ucode_blob.size = 0; >>> - ucode_blob.data = NULL; >>> - } >>> - else if ( ucode_mod.mod_end ) >>> - { >>> - bootstrap_unmap(); >>> - ucode_mod.mod_end = 0; >>> - } >>> - >>> - return 0; >>> -} >>> -__initcall(microcode_init); >> ... aren't used anywhere. bootstrap_unmap() is "just in case" (perhaps indeed >> a no-op at least nowadays), and the rest is field clobbering. I'm okay with the >> code change, so >> Acked-by: Jan Beulich <jbeulich@suse.com> >> yet I'd like to ask for the description to be "softened" some. > > As I said, this could be folded into patch 9, given this particular > arrangement of the series. I certainly don't mind the folding, but then I'd still like to see the description of the resulting patch before giving a (hopefully) unconditional ack. > The UAFs are apparent *because* the comment demonstrates a false line of > reasoning. > > ucode_mod literally is used after free. ucode=$n is genuinely buggy > today, because its a stash of a physical pointer across move_xen(). Maybe my problem is that the UAF is elsewhere, not in the code you delete? If so, it might help to simply point out where the actual bad use is. As it stands, microcode_init() doesn't have any uses (reads), only writes. What I agree with is that the comment there is at best misleading. Jan > ucode_blob stashes a virtual pointer. This was even noticed in > dc380df12acf ("x86/ucode: load microcode earlier on boot CPU") > > --- > It needs to rescan the modules in order to find the new virtual address > of the ucode blob because it changes during the boot process, e.g. > from 0x00000000010802fc to 0xffff83204dac52fc. > --- > > which highlighted the problem but duct-taped over it. > > ~Andrew
© 2016 - 2024 Red Hat, Inc.