diff options
author | Jan Beulich <JBeulich@suse.com> | 2014-08-08 17:20:09 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-08 18:57:19 -0400 |
commit | 6e85bab6bc1019f9b87c53b32da3ad7791e7ddf9 (patch) | |
tree | 7f83156bdd6fa857286d224a3443dc5f481cbd74 /drivers | |
parent | 3710f597aca5af4f605cda9793a8b2f0a42bc29a (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/rtc/rtc-efi.c | 32 |
1 files changed, 27 insertions, 5 deletions
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index c4c38431012e..8225b89de810 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c | |||
@@ -17,6 +17,7 @@ | |||
17 | 17 | ||
18 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/stringify.h> | ||
20 | #include <linux/time.h> | 21 | #include <linux/time.h> |
21 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
22 | #include <linux/rtc.h> | 23 | #include <linux/rtc.h> |
@@ -48,8 +49,8 @@ compute_wday(efi_time_t *eft) | |||
48 | int y; | 49 | int y; |
49 | int ndays = 0; | 50 | int ndays = 0; |
50 | 51 | ||
51 | if (eft->year < 1998) { | 52 | if (eft->year < EFI_RTC_EPOCH) { |
52 | pr_err("EFI year < 1998, invalid date\n"); | 53 | pr_err("EFI year < " __stringify(EFI_RTC_EPOCH) ", invalid date\n"); |
53 | return -1; | 54 | return -1; |
54 | } | 55 | } |
55 | 56 | ||
@@ -78,19 +79,36 @@ convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft) | |||
78 | eft->timezone = EFI_UNSPECIFIED_TIMEZONE; | 79 | eft->timezone = EFI_UNSPECIFIED_TIMEZONE; |
79 | } | 80 | } |
80 | 81 | ||
81 | static void | 82 | static bool |
82 | convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) | 83 | convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) |
83 | { | 84 | { |
84 | memset(wtime, 0, sizeof(*wtime)); | 85 | memset(wtime, 0, sizeof(*wtime)); |
86 | |||
87 | if (eft->second >= 60) | ||
88 | return false; | ||
85 | wtime->tm_sec = eft->second; | 89 | wtime->tm_sec = eft->second; |
90 | |||
91 | if (eft->minute >= 60) | ||
92 | return false; | ||
86 | wtime->tm_min = eft->minute; | 93 | wtime->tm_min = eft->minute; |
94 | |||
95 | if (eft->hour >= 24) | ||
96 | return false; | ||
87 | wtime->tm_hour = eft->hour; | 97 | wtime->tm_hour = eft->hour; |
98 | |||
99 | if (!eft->day || eft->day > 31) | ||
100 | return false; | ||
88 | wtime->tm_mday = eft->day; | 101 | wtime->tm_mday = eft->day; |
102 | |||
103 | if (!eft->month || eft->month > 12) | ||
104 | return false; | ||
89 | wtime->tm_mon = eft->month - 1; | 105 | wtime->tm_mon = eft->month - 1; |
90 | wtime->tm_year = eft->year - 1900; | 106 | wtime->tm_year = eft->year - 1900; |
91 | 107 | ||
92 | /* day of the week [0-6], Sunday=0 */ | 108 | /* day of the week [0-6], Sunday=0 */ |
93 | wtime->tm_wday = compute_wday(eft); | 109 | wtime->tm_wday = compute_wday(eft); |
110 | if (wtime->tm_wday < 0) | ||
111 | return false; | ||
94 | 112 | ||
95 | /* day in the year [1-365]*/ | 113 | /* day in the year [1-365]*/ |
96 | wtime->tm_yday = compute_yday(eft); | 114 | wtime->tm_yday = compute_yday(eft); |
@@ -106,6 +124,8 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime) | |||
106 | default: | 124 | default: |
107 | wtime->tm_isdst = -1; | 125 | wtime->tm_isdst = -1; |
108 | } | 126 | } |
127 | |||
128 | return true; | ||
109 | } | 129 | } |
110 | 130 | ||
111 | static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) | 131 | static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) |
@@ -122,7 +142,8 @@ static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) | |||
122 | if (status != EFI_SUCCESS) | 142 | if (status != EFI_SUCCESS) |
123 | return -EINVAL; | 143 | return -EINVAL; |
124 | 144 | ||
125 | convert_from_efi_time(&eft, &wkalrm->time); | 145 | if (!convert_from_efi_time(&eft, &wkalrm->time)) |
146 | return -EIO; | ||
126 | 147 | ||
127 | return rtc_valid_tm(&wkalrm->time); | 148 | return rtc_valid_tm(&wkalrm->time); |
128 | } | 149 | } |
@@ -163,7 +184,8 @@ static int efi_read_time(struct device *dev, struct rtc_time *tm) | |||
163 | return -EINVAL; | 184 | return -EINVAL; |
164 | } | 185 | } |
165 | 186 | ||
166 | convert_from_efi_time(&eft, tm); | 187 | if (!convert_from_efi_time(&eft, tm)) |
188 | return -EIO; | ||
167 | 189 | ||
168 | return rtc_valid_tm(tm); | 190 | return rtc_valid_tm(tm); |
169 | } | 191 | } |