diff options
author | Matthew Garrett <mjg@redhat.com> | 2012-05-03 16:50:46 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-07 11:56:38 -0400 |
commit | ecc53109a021559a49beb81c9c4c859012ee975e (patch) | |
tree | af0bceb7778e6a23bfff8075e6f8408bb06d7b06 | |
parent | 173c412ef8f37d781eb90f5cc8eeab118b987e68 (diff) |
efivars: Improve variable validation
commit 54b3a4d311c98ad94b737802a8b5f2c8c6bfd627 upstream.
Ben Hutchings pointed out that the validation in efivars was inadequate -
most obviously, an entry with size 0 would server as a DoS against the
kernel. Improve this based on his suggestions.
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/firmware/efivars.c | 46 |
1 files changed, 30 insertions, 16 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index a15c0d45f0a..e27d56c7cc1 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -167,18 +167,21 @@ utf16_strsize(efi_char16_t *data, unsigned long maxlength) | |||
167 | } | 167 | } |
168 | 168 | ||
169 | static bool | 169 | static bool |
170 | validate_device_path(struct efi_variable *var, int match, u8 *buffer, int len) | 170 | validate_device_path(struct efi_variable *var, int match, u8 *buffer, |
171 | unsigned long len) | ||
171 | { | 172 | { |
172 | struct efi_generic_dev_path *node; | 173 | struct efi_generic_dev_path *node; |
173 | int offset = 0; | 174 | int offset = 0; |
174 | 175 | ||
175 | node = (struct efi_generic_dev_path *)buffer; | 176 | node = (struct efi_generic_dev_path *)buffer; |
176 | 177 | ||
177 | while (offset < len) { | 178 | if (len < sizeof(*node)) |
178 | offset += node->length; | 179 | return false; |
179 | 180 | ||
180 | if (offset > len) | 181 | while (offset <= len - sizeof(*node) && |
181 | return false; | 182 | node->length >= sizeof(*node) && |
183 | node->length <= len - offset) { | ||
184 | offset += node->length; | ||
182 | 185 | ||
183 | if ((node->type == EFI_DEV_END_PATH || | 186 | if ((node->type == EFI_DEV_END_PATH || |
184 | node->type == EFI_DEV_END_PATH2) && | 187 | node->type == EFI_DEV_END_PATH2) && |
@@ -197,7 +200,8 @@ validate_device_path(struct efi_variable *var, int match, u8 *buffer, int len) | |||
197 | } | 200 | } |
198 | 201 | ||
199 | static bool | 202 | static bool |
200 | validate_boot_order(struct efi_variable *var, int match, u8 *buffer, int len) | 203 | validate_boot_order(struct efi_variable *var, int match, u8 *buffer, |
204 | unsigned long len) | ||
201 | { | 205 | { |
202 | /* An array of 16-bit integers */ | 206 | /* An array of 16-bit integers */ |
203 | if ((len % 2) != 0) | 207 | if ((len % 2) != 0) |
@@ -207,19 +211,27 @@ validate_boot_order(struct efi_variable *var, int match, u8 *buffer, int len) | |||
207 | } | 211 | } |
208 | 212 | ||
209 | static bool | 213 | static bool |
210 | validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len) | 214 | validate_load_option(struct efi_variable *var, int match, u8 *buffer, |
215 | unsigned long len) | ||
211 | { | 216 | { |
212 | u16 filepathlength; | 217 | u16 filepathlength; |
213 | int i, desclength = 0; | 218 | int i, desclength = 0, namelen; |
219 | |||
220 | namelen = utf16_strnlen(var->VariableName, sizeof(var->VariableName)); | ||
214 | 221 | ||
215 | /* Either "Boot" or "Driver" followed by four digits of hex */ | 222 | /* Either "Boot" or "Driver" followed by four digits of hex */ |
216 | for (i = match; i < match+4; i++) { | 223 | for (i = match; i < match+4; i++) { |
217 | if (hex_to_bin(var->VariableName[i] & 0xff) < 0) | 224 | if (var->VariableName[i] > 127 || |
225 | hex_to_bin(var->VariableName[i] & 0xff) < 0) | ||
218 | return true; | 226 | return true; |
219 | } | 227 | } |
220 | 228 | ||
221 | /* A valid entry must be at least 6 bytes */ | 229 | /* Reject it if there's 4 digits of hex and then further content */ |
222 | if (len < 6) | 230 | if (namelen > match + 4) |
231 | return false; | ||
232 | |||
233 | /* A valid entry must be at least 8 bytes */ | ||
234 | if (len < 8) | ||
223 | return false; | 235 | return false; |
224 | 236 | ||
225 | filepathlength = buffer[4] | buffer[5] << 8; | 237 | filepathlength = buffer[4] | buffer[5] << 8; |
@@ -228,7 +240,7 @@ validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len) | |||
228 | * There's no stored length for the description, so it has to be | 240 | * There's no stored length for the description, so it has to be |
229 | * found by hand | 241 | * found by hand |
230 | */ | 242 | */ |
231 | desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len) + 2; | 243 | desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2; |
232 | 244 | ||
233 | /* Each boot entry must have a descriptor */ | 245 | /* Each boot entry must have a descriptor */ |
234 | if (!desclength) | 246 | if (!desclength) |
@@ -250,7 +262,8 @@ validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len) | |||
250 | } | 262 | } |
251 | 263 | ||
252 | static bool | 264 | static bool |
253 | validate_uint16(struct efi_variable *var, int match, u8 *buffer, int len) | 265 | validate_uint16(struct efi_variable *var, int match, u8 *buffer, |
266 | unsigned long len) | ||
254 | { | 267 | { |
255 | /* A single 16-bit integer */ | 268 | /* A single 16-bit integer */ |
256 | if (len != 2) | 269 | if (len != 2) |
@@ -260,7 +273,8 @@ validate_uint16(struct efi_variable *var, int match, u8 *buffer, int len) | |||
260 | } | 273 | } |
261 | 274 | ||
262 | static bool | 275 | static bool |
263 | validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, int len) | 276 | validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, |
277 | unsigned long len) | ||
264 | { | 278 | { |
265 | int i; | 279 | int i; |
266 | 280 | ||
@@ -278,7 +292,7 @@ validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, int len) | |||
278 | struct variable_validate { | 292 | struct variable_validate { |
279 | char *name; | 293 | char *name; |
280 | bool (*validate)(struct efi_variable *var, int match, u8 *data, | 294 | bool (*validate)(struct efi_variable *var, int match, u8 *data, |
281 | int len); | 295 | unsigned long len); |
282 | }; | 296 | }; |
283 | 297 | ||
284 | static const struct variable_validate variable_validate[] = { | 298 | static const struct variable_validate variable_validate[] = { |
@@ -300,7 +314,7 @@ static const struct variable_validate variable_validate[] = { | |||
300 | }; | 314 | }; |
301 | 315 | ||
302 | static bool | 316 | static bool |
303 | validate_var(struct efi_variable *var, u8 *data, int len) | 317 | validate_var(struct efi_variable *var, u8 *data, unsigned long len) |
304 | { | 318 | { |
305 | int i; | 319 | int i; |
306 | u16 *unicode_name = var->VariableName; | 320 | u16 *unicode_name = var->VariableName; |