Date: Mon, 26 Mar 2018 20:54:57 +0000 From: bugzilla-noreply@freebsd.org To: freebsd-bugs@FreeBSD.org Subject: [Bug 192487] cpucontrol uses unsafe procedure to detect current microcode version Message-ID: <bug-192487-8-iqDJDECESo@https.bugs.freebsd.org/bugzilla/> In-Reply-To: <bug-192487-8@https.bugs.freebsd.org/bugzilla/> References: <bug-192487-8@https.bugs.freebsd.org/bugzilla/>
next in thread | previous in thread | raw e-mail | index | archive | help
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D192487 Dan Lukes <dan+freebsd.org@obluda.cz> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |dan+freebsd.org@obluda.cz --- Comment #4 from Dan Lukes <dan+freebsd.org@obluda.cz> --- Well, no one has provided the patch, so I will try one. Let's allow me to summarize first. --- ------------ --- part of=20 --- Intel=C2=AE 64 and IA-32 Architectures Software Developer=E2=80=99s Ma= nual Volume 3A: --- System Programming Guide, Part 1 --- paragraph 9.11.7.1 Determining the Signature --- ------------ CPUID returns a value in a model specific register in addition to its usual register return values. The semantics of CPUID cause it to deposit an updat= e ID value in the 64-bit model-specific register at address 08BH (IA32_BIOS_SIGN_ID). If no update is present in the processor, the value in= the MSR remains unmodified. The BIOS must pre-load a zero into the MSR before executing CPUID. If a read of the MSR at 8BH still returns zero after execu= ting CPUID, this indicates that no update is present. --- ------------ It's clear description - IA32_BIOS_SIGN_ID needs to be initialized to zero before CPUID or it's value can't be trusted after CPUID.=20 =3D=3D=3D=3D=3D CPUCONTROL =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- ------------ --- releng/11.1/usr.sbin/cpucontrol/intel.c 245491 2013-01-16 05:00:51Z ea= dler --- intel_update() --- ------------ Such function calls CPUID (line 116) with no prior initialization of IA32_BIOS_SIGN_ID, then read MSR_BIOS_SIGN (line 133) considering returned value "revision". Such revision is later compared with the version of patch= in attempt to decide the particular patch is newer than the one installed in C= PU. This implementation is INCORRECT. On unpatched procesor, the value of IA32_BIOS_SIGN_ID remain unchanged, thus the 'revision' is undefined random value. Because of it, the patch candidate may be considered old. In such ca= se, CPU remain in virgin state, with no attempt to update. I would like to note that the comment on sys/dev/cpuctl/cpuctl.c line 259 is unrelated to the issue we are speaking on. It's probably just copy&paste ec= ho of line 214 Suggested patch: initialize IA32_BIOS_SIGN_ID to 0 before CPUID; patch atta= ched Note - IA32_BIOS_SIGN_ID shall be initialized just before CPUID and read ju= st after it - to be on safe site. Or we are in risk someone else will modify IA32_BIOS_SIGN_ID in the mean time. Userland code can't do it reliably. So = the proposed patch need to be considered workaround. Final solution require new CPUCTL_x that will do WRMSR+CPUID+RDMSR sequence enclosed in critical_enter= () and critical_exit()=20=20 =3D=3D=3D=3D=3D CPUCTL_UPDATE =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D Despite this PR is not related to CPUCTL_UPDATE, I would like to make short notice claiming the implementation is broken as well and Mitchell Horne is = not true. But this broken implementation doesn't cause the eligible update is skipped. --- ------------ --- releng/11.1/sys/dev/cpuctl/cpuctl.c 330908 2018-03-14 04:00:00Z gordon --- ------------ CPUCTL_UPDATE, e.g. cpuctl_do_update() starts with CPUID (line 311) with no prior initialization of IA32_BIOS_SIGN_ID. Then it reads such MSR (line 365) into rev0 variable. On virgin CPU it will receive random garbage. Then the code do update (line 370) then it try to read IA32_BIOS_SIGN_ID into rev1 value. Unfortunately it uses CPUID with EAX=3D0 instead of (see example 9-9= in Intel's SDK) CPUID with EAX=3D1. So even the rev1 content is undefined. Fi= nally, it return 0 if rev1 > 0, otherwise it returns EEXIT.=20 Garbage in rev0 and rev1 may cause EEXIST error even in the case the update= has succeeded. Suggested patch: we can't initialize IA32_BIOS_SIGN_ID before CPUID (line 3= 11) as it's called in Intel/AMD common code and must not touch Intel-only MSR h= ere. Thus we need initialize initialize IA32_BIOS_SIGN_ID then call CPUID again = in update_intel(), CPUID with EAX=3D1 needs to be called to read rev1, and rel= ated RDMSR should be part of critical section; patch attached --=20 You are receiving this mail because: You are the assignee for the bug.=
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-192487-8-iqDJDECESo>