[PATCH 02/10] x86/ucode: Delete the microcode_init() initcall

Andrew Cooper posted 10 patches 3 weeks, 3 days ago
[PATCH 02/10] x86/ucode: Delete the microcode_init() initcall
Posted by Andrew Cooper 3 weeks, 3 days ago
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


Re: [PATCH 02/10] x86/ucode: Delete the microcode_init() initcall
Posted by Daniel P. Smith 2 weeks, 5 days ago
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>

Re: [PATCH 02/10] x86/ucode: Delete the microcode_init() initcall
Posted by Jan Beulich 3 weeks, 3 days ago
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
Re: [PATCH 02/10] x86/ucode: Delete the microcode_init() initcall
Posted by Andrew Cooper 3 weeks, 3 days ago
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

Re: [PATCH 02/10] x86/ucode: Delete the microcode_init() initcall
Posted by Jan Beulich 3 weeks, 2 days ago
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