]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/meta-ti-glsdk.git/blob - recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.17/0127-efi-Validate-UEFI-boot-variables.patch
linux-ti33x-psp 3.2: update to 3.2.18
[glsdk/meta-ti-glsdk.git] / recipes-kernel / linux / linux-ti33x-psp-3.2 / 3.2.17 / 0127-efi-Validate-UEFI-boot-variables.patch
1 From 620a8b56785ca32ec7cde5f632617d89409a21ba Mon Sep 17 00:00:00 2001
2 From: Matthew Garrett <mjg@redhat.com>
3 Date: Mon, 30 Apr 2012 16:11:30 -0400
4 Subject: [PATCH 127/165] efi: Validate UEFI boot variables
6 commit fec6c20b570bcf541e581fc97f2e0cbdb9725b98 upstream.
8 A common flaw in UEFI systems is a refusal to POST triggered by a malformed
9 boot variable. Once in this state, machines may only be restored by
10 reflashing their firmware with an external hardware device. While this is
11 obviously a firmware bug, the serious nature of the outcome suggests that
12 operating systems should filter their variable writes in order to prevent
13 a malicious user from rendering the machine unusable.
15 Signed-off-by: Matthew Garrett <mjg@redhat.com>
16 Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
17 Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
18 ---
19  drivers/firmware/efivars.c |  182 ++++++++++++++++++++++++++++++++++++++++++++
20  1 files changed, 182 insertions(+), 0 deletions(-)
22 diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
23 index b0a8117..150e4ec 100644
24 --- a/drivers/firmware/efivars.c
25 +++ b/drivers/firmware/efivars.c
26 @@ -191,6 +191,176 @@ utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len)
27         }
28  }
29  
30 +static bool
31 +validate_device_path(struct efi_variable *var, int match, u8 *buffer, int len)
32 +{
33 +       struct efi_generic_dev_path *node;
34 +       int offset = 0;
35 +
36 +       node = (struct efi_generic_dev_path *)buffer;
37 +
38 +       while (offset < len) {
39 +               offset += node->length;
40 +
41 +               if (offset > len)
42 +                       return false;
43 +
44 +               if ((node->type == EFI_DEV_END_PATH ||
45 +                    node->type == EFI_DEV_END_PATH2) &&
46 +                   node->sub_type == EFI_DEV_END_ENTIRE)
47 +                       return true;
48 +
49 +               node = (struct efi_generic_dev_path *)(buffer + offset);
50 +       }
51 +
52 +       /*
53 +        * If we're here then either node->length pointed past the end
54 +        * of the buffer or we reached the end of the buffer without
55 +        * finding a device path end node.
56 +        */
57 +       return false;
58 +}
59 +
60 +static bool
61 +validate_boot_order(struct efi_variable *var, int match, u8 *buffer, int len)
62 +{
63 +       /* An array of 16-bit integers */
64 +       if ((len % 2) != 0)
65 +               return false;
66 +
67 +       return true;
68 +}
69 +
70 +static bool
71 +validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len)
72 +{
73 +       u16 filepathlength;
74 +       int i, desclength = 0;
75 +
76 +       /* Either "Boot" or "Driver" followed by four digits of hex */
77 +       for (i = match; i < match+4; i++) {
78 +               if (hex_to_bin(var->VariableName[i] & 0xff) < 0)
79 +                       return true;
80 +       }
81 +
82 +       /* A valid entry must be at least 6 bytes */
83 +       if (len < 6)
84 +               return false;
85 +
86 +       filepathlength = buffer[4] | buffer[5] << 8;
87 +
88 +       /*
89 +        * There's no stored length for the description, so it has to be
90 +        * found by hand
91 +        */
92 +       desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len) + 2;
93 +
94 +       /* Each boot entry must have a descriptor */
95 +       if (!desclength)
96 +               return false;
97 +
98 +       /*
99 +        * If the sum of the length of the description, the claimed filepath
100 +        * length and the original header are greater than the length of the
101 +        * variable, it's malformed
102 +        */
103 +       if ((desclength + filepathlength + 6) > len)
104 +               return false;
106 +       /*
107 +        * And, finally, check the filepath
108 +        */
109 +       return validate_device_path(var, match, buffer + desclength + 6,
110 +                                   filepathlength);
111 +}
113 +static bool
114 +validate_uint16(struct efi_variable *var, int match, u8 *buffer, int len)
115 +{
116 +       /* A single 16-bit integer */
117 +       if (len != 2)
118 +               return false;
120 +       return true;
121 +}
123 +static bool
124 +validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, int len)
125 +{
126 +       int i;
128 +       for (i = 0; i < len; i++) {
129 +               if (buffer[i] > 127)
130 +                       return false;
132 +               if (buffer[i] == 0)
133 +                       return true;
134 +       }
136 +       return false;
137 +}
139 +struct variable_validate {
140 +       char *name;
141 +       bool (*validate)(struct efi_variable *var, int match, u8 *data,
142 +                        int len);
143 +};
145 +static const struct variable_validate variable_validate[] = {
146 +       { "BootNext", validate_uint16 },
147 +       { "BootOrder", validate_boot_order },
148 +       { "DriverOrder", validate_boot_order },
149 +       { "Boot*", validate_load_option },
150 +       { "Driver*", validate_load_option },
151 +       { "ConIn", validate_device_path },
152 +       { "ConInDev", validate_device_path },
153 +       { "ConOut", validate_device_path },
154 +       { "ConOutDev", validate_device_path },
155 +       { "ErrOut", validate_device_path },
156 +       { "ErrOutDev", validate_device_path },
157 +       { "Timeout", validate_uint16 },
158 +       { "Lang", validate_ascii_string },
159 +       { "PlatformLang", validate_ascii_string },
160 +       { "", NULL },
161 +};
163 +static bool
164 +validate_var(struct efi_variable *var, u8 *data, int len)
165 +{
166 +       int i;
167 +       u16 *unicode_name = var->VariableName;
169 +       for (i = 0; variable_validate[i].validate != NULL; i++) {
170 +               const char *name = variable_validate[i].name;
171 +               int match;
173 +               for (match = 0; ; match++) {
174 +                       char c = name[match];
175 +                       u16 u = unicode_name[match];
177 +                       /* All special variables are plain ascii */
178 +                       if (u > 127)
179 +                               return true;
181 +                       /* Wildcard in the matching name means we've matched */
182 +                       if (c == '*')
183 +                               return variable_validate[i].validate(var,
184 +                                                            match, data, len);
186 +                       /* Case sensitive match */
187 +                       if (c != u)
188 +                               break;
190 +                       /* Reached the end of the string while matching */
191 +                       if (!c)
192 +                               return variable_validate[i].validate(var,
193 +                                                            match, data, len);
194 +               }
195 +       }
197 +       return true;
198 +}
200  static efi_status_t
201  get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
202  {
203 @@ -324,6 +494,12 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
204                 return -EINVAL;
205         }
206  
207 +       if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 ||
208 +           validate_var(new_var, new_var->Data, new_var->DataSize) == false) {
209 +               printk(KERN_ERR "efivars: Malformed variable content\n");
210 +               return -EINVAL;
211 +       }
213         spin_lock(&efivars->lock);
214         status = efivars->ops->set_variable(new_var->VariableName,
215                                             &new_var->VendorGuid,
216 @@ -624,6 +800,12 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
217         if (!capable(CAP_SYS_ADMIN))
218                 return -EACCES;
219  
220 +       if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 ||
221 +           validate_var(new_var, new_var->Data, new_var->DataSize) == false) {
222 +               printk(KERN_ERR "efivars: Malformed variable content\n");
223 +               return -EINVAL;
224 +       }
226         spin_lock(&efivars->lock);
227  
228         /*
229 -- 
230 1.7.7.6