diff options
author | Jann Horn <jannh@google.com> | 2019-04-04 07:11:28 -0400 |
---|---|---|
committer | Borislav Petkov <bp@suse.de> | 2019-04-10 16:40:25 -0400 |
commit | 7e94a7b659eefedda82cde97229a26f319fb1182 (patch) | |
tree | 2a2ecc5eb53754522f0c3112bb6626c3f23ed13e /arch/x86/kernel/cpu/microcode/intel.c | |
parent | 79a3aaa7b82e3106be97842dedfd8429248896e6 (diff) |
x86/microcode/intel: Refactor Intel microcode blob loading
Change generic_load_microcode() to use the iov_iter API instead of a
clumsy open-coded version which has to pay attention to __user data
or kernel data, depending on the loading method. This allows to avoid
explicit casting between user and kernel pointers.
Because the iov_iter API makes it hard to read the same location twice,
as a side effect, also fix a double-read of the microcode header (which
could e.g. lead to out-of-bounds reads in microcode_sanity_check()).
Not that it matters much, only root is allowed to load microcode
anyway...
[ bp: Massage a bit, sort function-local variables. ]
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: x86-ml <x86@kernel.org>
Link: https://lkml.kernel.org/r/20190404111128.131157-1-jannh@google.com
Diffstat (limited to 'arch/x86/kernel/cpu/microcode/intel.c')
-rw-r--r-- | arch/x86/kernel/cpu/microcode/intel.c | 71 |
1 files changed, 36 insertions, 35 deletions
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c index 16936a24795c..a44bdbe7c55e 100644 --- a/arch/x86/kernel/cpu/microcode/intel.c +++ b/arch/x86/kernel/cpu/microcode/intel.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/kernel.h> | 31 | #include <linux/kernel.h> |
32 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
33 | #include <linux/cpu.h> | 33 | #include <linux/cpu.h> |
34 | #include <linux/uio.h> | ||
34 | #include <linux/mm.h> | 35 | #include <linux/mm.h> |
35 | 36 | ||
36 | #include <asm/microcode_intel.h> | 37 | #include <asm/microcode_intel.h> |
@@ -861,32 +862,33 @@ out: | |||
861 | return ret; | 862 | return ret; |
862 | } | 863 | } |
863 | 864 | ||
864 | static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, | 865 | static enum ucode_state generic_load_microcode(int cpu, struct iov_iter *iter) |
865 | int (*get_ucode_data)(void *, const void *, size_t)) | ||
866 | { | 866 | { |
867 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; | 867 | struct ucode_cpu_info *uci = ucode_cpu_info + cpu; |
868 | u8 *ucode_ptr = data, *new_mc = NULL, *mc = NULL; | ||
869 | int new_rev = uci->cpu_sig.rev; | ||
870 | unsigned int leftover = size; | ||
871 | unsigned int curr_mc_size = 0, new_mc_size = 0; | 868 | unsigned int curr_mc_size = 0, new_mc_size = 0; |
872 | unsigned int csig, cpf; | ||
873 | enum ucode_state ret = UCODE_OK; | 869 | enum ucode_state ret = UCODE_OK; |
870 | int new_rev = uci->cpu_sig.rev; | ||
871 | u8 *new_mc = NULL, *mc = NULL; | ||
872 | unsigned int csig, cpf; | ||
874 | 873 | ||
875 | while (leftover) { | 874 | while (iov_iter_count(iter)) { |
876 | struct microcode_header_intel mc_header; | 875 | struct microcode_header_intel mc_header; |
877 | unsigned int mc_size; | 876 | unsigned int mc_size, data_size; |
877 | u8 *data; | ||
878 | 878 | ||
879 | if (leftover < sizeof(mc_header)) { | 879 | if (!copy_from_iter_full(&mc_header, sizeof(mc_header), iter)) { |
880 | pr_err("error! Truncated header in microcode data file\n"); | 880 | pr_err("error! Truncated or inaccessible header in microcode data file\n"); |
881 | break; | 881 | break; |
882 | } | 882 | } |
883 | 883 | ||
884 | if (get_ucode_data(&mc_header, ucode_ptr, sizeof(mc_header))) | ||
885 | break; | ||
886 | |||
887 | mc_size = get_totalsize(&mc_header); | 884 | mc_size = get_totalsize(&mc_header); |
888 | if (!mc_size || mc_size > leftover) { | 885 | if (mc_size < sizeof(mc_header)) { |
889 | pr_err("error! Bad data in microcode data file\n"); | 886 | pr_err("error! Bad data in microcode data file (totalsize too small)\n"); |
887 | break; | ||
888 | } | ||
889 | data_size = mc_size - sizeof(mc_header); | ||
890 | if (data_size > iov_iter_count(iter)) { | ||
891 | pr_err("error! Bad data in microcode data file (truncated file?)\n"); | ||
890 | break; | 892 | break; |
891 | } | 893 | } |
892 | 894 | ||
@@ -899,7 +901,9 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, | |||
899 | curr_mc_size = mc_size; | 901 | curr_mc_size = mc_size; |
900 | } | 902 | } |
901 | 903 | ||
902 | if (get_ucode_data(mc, ucode_ptr, mc_size) || | 904 | memcpy(mc, &mc_header, sizeof(mc_header)); |
905 | data = mc + sizeof(mc_header); | ||
906 | if (!copy_from_iter_full(data, data_size, iter) || | ||
903 | microcode_sanity_check(mc, 1) < 0) { | 907 | microcode_sanity_check(mc, 1) < 0) { |
904 | break; | 908 | break; |
905 | } | 909 | } |
@@ -914,14 +918,11 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, | |||
914 | mc = NULL; /* trigger new vmalloc */ | 918 | mc = NULL; /* trigger new vmalloc */ |
915 | ret = UCODE_NEW; | 919 | ret = UCODE_NEW; |
916 | } | 920 | } |
917 | |||
918 | ucode_ptr += mc_size; | ||
919 | leftover -= mc_size; | ||
920 | } | 921 | } |
921 | 922 | ||
922 | vfree(mc); | 923 | vfree(mc); |
923 | 924 | ||
924 | if (leftover) { | 925 | if (iov_iter_count(iter)) { |
925 | vfree(new_mc); | 926 | vfree(new_mc); |
926 | return UCODE_ERROR; | 927 | return UCODE_ERROR; |
927 | } | 928 | } |
@@ -945,12 +946,6 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size, | |||
945 | return ret; | 946 | return ret; |
946 | } | 947 | } |
947 | 948 | ||
948 | static int get_ucode_fw(void *to, const void *from, size_t n) | ||
949 | { | ||
950 | memcpy(to, from, n); | ||
951 | return 0; | ||
952 | } | ||
953 | |||
954 | static bool is_blacklisted(unsigned int cpu) | 949 | static bool is_blacklisted(unsigned int cpu) |
955 | { | 950 | { |
956 | struct cpuinfo_x86 *c = &cpu_data(cpu); | 951 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
@@ -977,10 +972,12 @@ static bool is_blacklisted(unsigned int cpu) | |||
977 | static enum ucode_state request_microcode_fw(int cpu, struct device *device, | 972 | static enum ucode_state request_microcode_fw(int cpu, struct device *device, |
978 | bool refresh_fw) | 973 | bool refresh_fw) |
979 | { | 974 | { |
980 | char name[30]; | ||
981 | struct cpuinfo_x86 *c = &cpu_data(cpu); | 975 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
982 | const struct firmware *firmware; | 976 | const struct firmware *firmware; |
977 | struct iov_iter iter; | ||
983 | enum ucode_state ret; | 978 | enum ucode_state ret; |
979 | struct kvec kvec; | ||
980 | char name[30]; | ||
984 | 981 | ||
985 | if (is_blacklisted(cpu)) | 982 | if (is_blacklisted(cpu)) |
986 | return UCODE_NFOUND; | 983 | return UCODE_NFOUND; |
@@ -993,26 +990,30 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device, | |||
993 | return UCODE_NFOUND; | 990 | return UCODE_NFOUND; |
994 | } | 991 | } |
995 | 992 | ||
996 | ret = generic_load_microcode(cpu, (void *)firmware->data, | 993 | kvec.iov_base = (void *)firmware->data; |
997 | firmware->size, &get_ucode_fw); | 994 | kvec.iov_len = firmware->size; |
995 | iov_iter_kvec(&iter, WRITE, &kvec, 1, firmware->size); | ||
996 | ret = generic_load_microcode(cpu, &iter); | ||
998 | 997 | ||
999 | release_firmware(firmware); | 998 | release_firmware(firmware); |
1000 | 999 | ||
1001 | return ret; | 1000 | return ret; |
1002 | } | 1001 | } |
1003 | 1002 | ||
1004 | static int get_ucode_user(void *to, const void *from, size_t n) | ||
1005 | { | ||
1006 | return copy_from_user(to, from, n); | ||
1007 | } | ||
1008 | |||
1009 | static enum ucode_state | 1003 | static enum ucode_state |
1010 | request_microcode_user(int cpu, const void __user *buf, size_t size) | 1004 | request_microcode_user(int cpu, const void __user *buf, size_t size) |
1011 | { | 1005 | { |
1006 | struct iov_iter iter; | ||
1007 | struct iovec iov; | ||
1008 | |||
1012 | if (is_blacklisted(cpu)) | 1009 | if (is_blacklisted(cpu)) |
1013 | return UCODE_NFOUND; | 1010 | return UCODE_NFOUND; |
1014 | 1011 | ||
1015 | return generic_load_microcode(cpu, (void *)buf, size, &get_ucode_user); | 1012 | iov.iov_base = (void __user *)buf; |
1013 | iov.iov_len = size; | ||
1014 | iov_iter_init(&iter, WRITE, &iov, 1, size); | ||
1015 | |||
1016 | return generic_load_microcode(cpu, &iter); | ||
1016 | } | 1017 | } |
1017 | 1018 | ||
1018 | static struct microcode_ops microcode_intel_ops = { | 1019 | static struct microcode_ops microcode_intel_ops = { |