* [PATCH] x86/microcode: Change checksum to u32
@ 2016-02-27 11:01 Chris Bainbridge
2016-02-27 17:51 ` Borislav Petkov
2016-03-16 10:52 ` Pavel Machek
0 siblings, 2 replies; 7+ messages in thread
From: Chris Bainbridge @ 2016-02-27 11:01 UTC (permalink / raw)
To: bp; +Cc: Chris Bainbridge, x86, linux-kernel
Checksum should be unsigned 32-bit otherwise the calculation overflows
resulting in undefined behaviour:
[ 0.000000] ================================================================================
[ 0.000000] UBSAN: Undefined behaviour in arch/x86/kernel/cpu/microcode/intel_lib.c:105:12
[ 0.000000] signed integer overflow:
[ 0.000000] -1500151068 + -2125470173 cannot be represented in type 'int'
[ 0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 4.5.0-rc5+ #495
[ 0.000000] 0000000000000086 0000000000000086 0000000000000000 ffffffff83203968
[ 0.000000] ffffffff81b30952 ffffffff834c43b8 ffffffff83203998 ffffffff814fe623
[ 0.000000] ffffffff83203980 ffffffff81bcdf2d ffffffff8339a448 ffffffff83203a08
[ 0.000000] Call Trace:
[ 0.000000] [<ffffffff81b30952>] dump_stack+0x4e/0x6c
[ 0.000000] [<ffffffff814fe623>] ? inotify_ioctl+0x43/0x1c0
[ 0.000000] [<ffffffff81bcdf2d>] ubsan_epilogue+0xd/0x40
[ 0.000000] [<ffffffff81bce2fd>] handle_overflow+0xbd/0xe0
[ 0.000000] [<ffffffff81bce32e>] __ubsan_handle_add_overflow+0xe/0x10
[ 0.000000] [<ffffffff81148e45>] microcode_sanity_check+0x405/0x590
[ 0.000000] [<ffffffff8549b01b>] get_matching_model_microcode.isra.2.constprop.8+0xa5/0x345
[ 0.000000] [<ffffffff8548615d>] ? early_idt_handler_common+0x3d/0xae
[ 0.000000] [<ffffffff81b50962>] ? strlcpy+0x52/0xa0
[ 0.000000] [<ffffffff81b30ce1>] ? find_cpio_data+0x371/0x510
[ 0.000000] [<ffffffff8549b461>] load_ucode_intel_bsp+0xa1/0xe5
[ 0.000000] [<ffffffff8549aea8>] load_ucode_bsp+0xdf/0xf3
[ 0.000000] [<ffffffff8549aea8>] ? load_ucode_bsp+0xdf/0xf3
[ 0.000000] [<ffffffff8548671c>] x86_64_start_kernel+0xd1/0xee
[ 0.000000] ================================================================================
Signed-off-by: Chris Bainbridge <chris.bainbridge@gmail.com>
---
arch/x86/kernel/cpu/microcode/intel_lib.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/kernel/cpu/microcode/intel_lib.c b/arch/x86/kernel/cpu/microcode/intel_lib.c
index b96896bcbdaf..2bef93ff2e3f 100644
--- a/arch/x86/kernel/cpu/microcode/intel_lib.c
+++ b/arch/x86/kernel/cpu/microcode/intel_lib.c
@@ -49,7 +49,7 @@ int microcode_sanity_check(void *mc, int print_err)
unsigned long total_size, data_size, ext_table_size;
struct microcode_header_intel *mc_header = mc;
struct extended_sigtable *ext_header = NULL;
- int sum, orig_sum, ext_sigcount = 0, i;
+ u32 sum, orig_sum, ext_sigcount = 0, i;
struct extended_signature *ext_sig;
total_size = get_totalsize(mc_header);
--
2.1.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] x86/microcode: Change checksum to u32
2016-02-27 11:01 [PATCH] x86/microcode: Change checksum to u32 Chris Bainbridge
@ 2016-02-27 17:51 ` Borislav Petkov
2016-02-27 18:23 ` Chris Bainbridge
2016-02-27 20:12 ` Henrique de Moraes Holschuh
2016-03-16 10:52 ` Pavel Machek
1 sibling, 2 replies; 7+ messages in thread
From: Borislav Petkov @ 2016-02-27 17:51 UTC (permalink / raw)
To: Chris Bainbridge; +Cc: x86, linux-kernel
On Sat, Feb 27, 2016 at 11:01:47AM +0000, Chris Bainbridge wrote:
> Checksum should be unsigned 32-bit otherwise the calculation overflows
> resulting in undefined behaviour:
>
> [ 0.000000] ================================================================================
> [ 0.000000] UBSAN: Undefined behaviour in arch/x86/kernel/cpu/microcode/intel_lib.c:105:12
> [ 0.000000] signed integer overflow:
> [ 0.000000] -1500151068 + -2125470173 cannot be represented in type 'int'
So I've been staring at this error message for a while now and I'm
having hard time understanding what it is telling me. Let's look at all
three ints:
* sum is computed here:
sum = orig_sum
- (mc_header->sig + mc_header->pf + mc_header->cksum)
+ (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
and checks mc_header against each extended signature. If it overflows, we abort:
if (sum) {
if (print_err)
pr_err("aborting, bad checksum\n");
* orig_sum in the above sum can only be 0:
if (orig_sum) {
if (print_err)
pr_err("aborting, bad checksum\n");
return -EINVAL;
and it adds a bunch of integers which can overflow, sure, but if it
overflows, we exit early.
* and
ext_sigcount = ext_header->count;
so it is some count of extended signatures.
So what is ubsan complaining about?
/me is confused.
--
Regards/Gruss,
Boris.
ECO tip #101: Trim your mails when you reply.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] x86/microcode: Change checksum to u32
2016-02-27 17:51 ` Borislav Petkov
@ 2016-02-27 18:23 ` Chris Bainbridge
2016-02-27 19:44 ` Henrique de Moraes Holschuh
2016-02-27 22:29 ` Borislav Petkov
2016-02-27 20:12 ` Henrique de Moraes Holschuh
1 sibling, 2 replies; 7+ messages in thread
From: Chris Bainbridge @ 2016-02-27 18:23 UTC (permalink / raw)
To: Borislav Petkov; +Cc: x86, linux-kernel
On Sat, Feb 27, 2016 at 06:51:18PM +0100, Borislav Petkov wrote:
> On Sat, Feb 27, 2016 at 11:01:47AM +0000, Chris Bainbridge wrote:
> > Checksum should be unsigned 32-bit otherwise the calculation overflows
> > resulting in undefined behaviour:
> >
> > [ 0.000000] ================================================================================
> > [ 0.000000] UBSAN: Undefined behaviour in arch/x86/kernel/cpu/microcode/intel_lib.c:105:12
> > [ 0.000000] signed integer overflow:
> > [ 0.000000] -1500151068 + -2125470173 cannot be represented in type 'int'
>
> So I've been staring at this error message for a while now and I'm
> having hard time understanding what it is telling me. Let's look at all
> three ints:
>
> * sum is computed here:
>
> sum = orig_sum
> - (mc_header->sig + mc_header->pf + mc_header->cksum)
> + (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
>
> and checks mc_header against each extended signature. If it overflows, we abort:
>
> if (sum) {
> if (print_err)
> pr_err("aborting, bad checksum\n");
>
>
> * orig_sum in the above sum can only be 0:
>
> if (orig_sum) {
> if (print_err)
> pr_err("aborting, bad checksum\n");
> return -EINVAL;
>
> and it adds a bunch of integers which can overflow, sure, but if it
> overflows, we exit early.
>
> * and
>
> ext_sigcount = ext_header->count;
>
> so it is some count of extended signatures.
>
> So what is ubsan complaining about?
/* calculate the checksum */
orig_sum = 0;
i = (MC_HEADER_SIZE + data_size) / DWSIZE;
while (i--)
orig_sum += ((int *)mc)[i];
The checksum is the additive sum of all the 32-bit values, but 32-bit
addition overflow is undefined for signed ints. For comparison the
checksum function from iucode-tool uses u32:
intel_ucode_status_t intel_ucode_check_microcode(const void * const uc,
int strict)
{
...
uint32_t sum, orig_sum;
unsigned int i;
uint32_t *p;
...
/* Calculate the checksum. We exclude the extended table as it
* also has to have a zero checksum, in order to get better
* coverage */
orig_sum = 0;
i = (INTEL_UC_V1_HEADER_SIZE + data_size) / sizeof(uint32_t);
p = (uint32_t *)uc;
while (i--) {
orig_sum += *p;
p++;
}
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] x86/microcode: Change checksum to u32
2016-02-27 18:23 ` Chris Bainbridge
@ 2016-02-27 19:44 ` Henrique de Moraes Holschuh
2016-02-27 22:29 ` Borislav Petkov
1 sibling, 0 replies; 7+ messages in thread
From: Henrique de Moraes Holschuh @ 2016-02-27 19:44 UTC (permalink / raw)
To: Chris Bainbridge; +Cc: Borislav Petkov, x86, linux-kernel
On Sat, 27 Feb 2016, Chris Bainbridge wrote:
> > So what is ubsan complaining about?
>
> /* calculate the checksum */
> orig_sum = 0;
> i = (MC_HEADER_SIZE + data_size) / DWSIZE;
> while (i--)
> orig_sum += ((int *)mc)[i];
>
> The checksum is the additive sum of all the 32-bit values, but 32-bit
> addition overflow is undefined for signed ints. For comparison the
> checksum function from iucode-tool uses u32:
>
> intel_ucode_status_t intel_ucode_check_microcode(const void * const uc,
> int strict)
> {
> ...
>
> uint32_t sum, orig_sum;
> unsigned int i;
> uint32_t *p;
>
> ...
>
> /* Calculate the checksum. We exclude the extended table as it
> * also has to have a zero checksum, in order to get better
> * coverage */
> orig_sum = 0;
> i = (INTEL_UC_V1_HEADER_SIZE + data_size) / sizeof(uint32_t);
> p = (uint32_t *)uc;
> while (i--) {
> orig_sum += *p;
> p++;
> }
iucode-tool does it that way because:
[Excerpt from the Intel 64 and IA‐32 Architectures Software Developer's
Manual, Volume 3A: System Programming Guide]:
9.11.5 Microcode Update Checksum
Each microcode update contains a DWORD checksum located in the
update header. It is software’s responsibility to ensure that a
microcode update is not corrupt. To check for a corrupt microcode
update, software must perform a unsigned DWORD (32-bit) checksum of
the microcode update.
Even though some fields are signed, the checksum procedure treats
all DWORDs as unsigned. Microcode updates with a header version
equal to 00000001H must sum all DWORDs that comprise the microcode
update. A valid checksum check will yield a value of 00000000H. Any
other value indicates the microcode update is corrupt and should not
be loaded."
And "sum" will always overflow 32 bits on valid microcode, as the end result
must be zero and has to warp-around for that to happen. Since the checksum
algorithm really needs modulo-2^32 addition, it is best to be upfront about
it...
--
"One disk to rule them all, One disk to find them. One disk to bring
them all and in the darkness grind them. In the Land of Redmond
where the shadows lie." -- The Silicon Valley Tarot
Henrique Holschuh
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] x86/microcode: Change checksum to u32
2016-02-27 17:51 ` Borislav Petkov
2016-02-27 18:23 ` Chris Bainbridge
@ 2016-02-27 20:12 ` Henrique de Moraes Holschuh
1 sibling, 0 replies; 7+ messages in thread
From: Henrique de Moraes Holschuh @ 2016-02-27 20:12 UTC (permalink / raw)
To: Borislav Petkov; +Cc: Chris Bainbridge, x86, linux-kernel
On Sat, 27 Feb 2016, Borislav Petkov wrote:
> * sum is computed here:
>
> sum = orig_sum
> - (mc_header->sig + mc_header->pf + mc_header->cksum)
> + (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
Well, orig_sum is always zero, because we exit early when it isn't. sum
really ought to be u32 in the first place (the only reason the code works is
because gcc isn't that insane), and the other fields should have been cast
to u32 as well before being added, for clarity if nothing else.
> and it adds a bunch of integers which can overflow, sure, but if it
> overflows, we exit early.
There is no reason why "sig + pf + cksum" wouldn't overflow 32 bits... in
fact, they often (always?) do on valid microcode.
And we need all of the intel-microcode-checksum-related arithmetic to be
done modulo-2^32, because it is expected to overflow *and* warp around when
it does so.
--
"One disk to rule them all, One disk to find them. One disk to bring
them all and in the darkness grind them. In the Land of Redmond
where the shadows lie." -- The Silicon Valley Tarot
Henrique Holschuh
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] x86/microcode: Change checksum to u32
2016-02-27 18:23 ` Chris Bainbridge
2016-02-27 19:44 ` Henrique de Moraes Holschuh
@ 2016-02-27 22:29 ` Borislav Petkov
1 sibling, 0 replies; 7+ messages in thread
From: Borislav Petkov @ 2016-02-27 22:29 UTC (permalink / raw)
To: Chris Bainbridge; +Cc: x86, linux-kernel
On Sat, Feb 27, 2016 at 06:23:29PM +0000, Chris Bainbridge wrote:
> /* calculate the checksum */
> orig_sum = 0;
> i = (MC_HEADER_SIZE + data_size) / DWSIZE;
> while (i--)
> orig_sum += ((int *)mc)[i];
Ok, since SDM says that all fields should be treated as unsigneds when
doing the checksum verification, that cast above should be (u32 *) too.
Also, the extended table signature should be fixed to use u32s too:
ext_tablep = (int *)ext_header;
i = ext_table_size / sizeof(u32);
while (i--)
ext_table_sum += ext_tablep[i];
...
Care to complete your patch please?
Thanks.
--
Regards/Gruss,
Boris.
ECO tip #101: Trim your mails when you reply.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] x86/microcode: Change checksum to u32
2016-02-27 11:01 [PATCH] x86/microcode: Change checksum to u32 Chris Bainbridge
2016-02-27 17:51 ` Borislav Petkov
@ 2016-03-16 10:52 ` Pavel Machek
1 sibling, 0 replies; 7+ messages in thread
From: Pavel Machek @ 2016-03-16 10:52 UTC (permalink / raw)
To: Chris Bainbridge; +Cc: bp, x86, linux-kernel
Hi!
> Checksum should be unsigned 32-bit otherwise the calculation overflows
> resulting in undefined behaviour:
> @@ -49,7 +49,7 @@ int microcode_sanity_check(void *mc, int print_err)
> unsigned long total_size, data_size, ext_table_size;
> struct microcode_header_intel *mc_header = mc;
> struct extended_sigtable *ext_header = NULL;
> - int sum, orig_sum, ext_sigcount = 0, i;
> + u32 sum, orig_sum, ext_sigcount = 0, i;
Ok, but what about i and ext_sigcount?
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2016-03-16 10:52 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-02-27 11:01 [PATCH] x86/microcode: Change checksum to u32 Chris Bainbridge
2016-02-27 17:51 ` Borislav Petkov
2016-02-27 18:23 ` Chris Bainbridge
2016-02-27 19:44 ` Henrique de Moraes Holschuh
2016-02-27 22:29 ` Borislav Petkov
2016-02-27 20:12 ` Henrique de Moraes Holschuh
2016-03-16 10:52 ` Pavel Machek
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox