Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers/rtc/rtc-efi.c: check for invalid data coming back from UEFI

In particular seeing zero in eft->month is problematic, as it results in
-1 (converted to unsigned int, i.e. yielding 0xffffffff) getting passed
to rtc_year_days(), where the value gets used as an array index
(normally resulting in a crash). This was observed with the driver
enabled on x86 on some Fujitsu system (with possibly not up to date
firmware, but anyway).

Perhaps efi_read_alarm() should not fail if neither enabled nor pending
are set, but the returned time is invalid?

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reported-by: Raymund Will <rw@suse.de>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Jingoo Han <jg1.han@samsung.com>
Acked-by: Lee, Chun-Yi <jlee@suse.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Jan Beulich and committed by
Linus Torvalds
6e85bab6 3710f597

+27 -5
+27 -5
drivers/rtc/rtc-efi.c
··· 17 17 18 18 #include <linux/kernel.h> 19 19 #include <linux/module.h> 20 + #include <linux/stringify.h> 20 21 #include <linux/time.h> 21 22 #include <linux/platform_device.h> 22 23 #include <linux/rtc.h> ··· 49 48 int y; 50 49 int ndays = 0; 51 50 52 - if (eft->year < 1998) { 53 - pr_err("EFI year < 1998, invalid date\n"); 51 + if (eft->year < EFI_RTC_EPOCH) { 52 + pr_err("EFI year < " __stringify(EFI_RTC_EPOCH) ", invalid date\n"); 54 53 return -1; 55 54 } 56 55 ··· 79 78 eft->timezone = EFI_UNSPECIFIED_TIMEZONE; 80 79 } 81 80 82 - static void 81 + static bool 83 82 convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) 84 83 { 85 84 memset(wtime, 0, sizeof(*wtime)); 85 + 86 + if (eft->second >= 60) 87 + return false; 86 88 wtime->tm_sec = eft->second; 89 + 90 + if (eft->minute >= 60) 91 + return false; 87 92 wtime->tm_min = eft->minute; 93 + 94 + if (eft->hour >= 24) 95 + return false; 88 96 wtime->tm_hour = eft->hour; 97 + 98 + if (!eft->day || eft->day > 31) 99 + return false; 89 100 wtime->tm_mday = eft->day; 101 + 102 + if (!eft->month || eft->month > 12) 103 + return false; 90 104 wtime->tm_mon = eft->month - 1; 91 105 wtime->tm_year = eft->year - 1900; 92 106 93 107 /* day of the week [0-6], Sunday=0 */ 94 108 wtime->tm_wday = compute_wday(eft); 109 + if (wtime->tm_wday < 0) 110 + return false; 95 111 96 112 /* day in the year [1-365]*/ 97 113 wtime->tm_yday = compute_yday(eft); ··· 124 106 default: 125 107 wtime->tm_isdst = -1; 126 108 } 109 + 110 + return true; 127 111 } 128 112 129 113 static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) ··· 142 122 if (status != EFI_SUCCESS) 143 123 return -EINVAL; 144 124 145 - convert_from_efi_time(&eft, &wkalrm->time); 125 + if (!convert_from_efi_time(&eft, &wkalrm->time)) 126 + return -EIO; 146 127 147 128 return rtc_valid_tm(&wkalrm->time); 148 129 } ··· 184 163 return -EINVAL; 185 164 } 186 165 187 - convert_from_efi_time(&eft, tm); 166 + if (!convert_from_efi_time(&eft, tm)) 167 + return -EIO; 188 168 189 169 return rtc_valid_tm(tm); 190 170 }