aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Garrett <mjg@redhat.com>2012-05-03 16:50:46 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-05-07 11:56:38 -0400
commitecc53109a021559a49beb81c9c4c859012ee975e (patch)
treeaf0bceb7778e6a23bfff8075e6f8408bb06d7b06
parent173c412ef8f37d781eb90f5cc8eeab118b987e68 (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.c46
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
169static bool 169static bool
170validate_device_path(struct efi_variable *var, int match, u8 *buffer, int len) 170validate_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
199static bool 202static bool
200validate_boot_order(struct efi_variable *var, int match, u8 *buffer, int len) 203validate_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
209static bool 213static bool
210validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len) 214validate_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
252static bool 264static bool
253validate_uint16(struct efi_variable *var, int match, u8 *buffer, int len) 265validate_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
262static bool 275static bool
263validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, int len) 276validate_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)
278struct variable_validate { 292struct 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
284static const struct variable_validate variable_validate[] = { 298static const struct variable_validate variable_validate[] = {
@@ -300,7 +314,7 @@ static const struct variable_validate variable_validate[] = {
300}; 314};
301 315
302static bool 316static bool
303validate_var(struct efi_variable *var, u8 *data, int len) 317validate_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;