aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2011-11-28 03:44:14 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-11-29 06:53:53 -0500
commit4f718a29fe4908c2cea782f751e9805319684e2b (patch)
treee4d9d630d4200a40bcc96b2a168828f8fb4ed1df /drivers/firmware
parentfc8e6e8668e74fbf8e00d6e143d7f43b20f73f32 (diff)
firmware: Sigma: Prevent out of bounds memory access
The SigmaDSP firmware loader currently does not perform enough boundary size checks when processing the firmware. As a result it is possible that a malformed firmware can cause an out of bounds memory access. This patch adds checks which ensure that both the action header and the payload are completely inside the firmware data boundaries before processing them. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Mike Frysinger <vapier@gentoo.org> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Cc: stable@kernel.org
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/sigma.c76
1 files changed, 55 insertions, 21 deletions
diff --git a/drivers/firmware/sigma.c b/drivers/firmware/sigma.c
index f10fc521951b..c780baa59ed9 100644
--- a/drivers/firmware/sigma.c
+++ b/drivers/firmware/sigma.c
@@ -14,13 +14,34 @@
14#include <linux/module.h> 14#include <linux/module.h>
15#include <linux/sigma.h> 15#include <linux/sigma.h>
16 16
17/* Return: 0==OK, <0==error, =1 ==no more actions */ 17static size_t sigma_action_size(struct sigma_action *sa)
18{
19 size_t payload = 0;
20
21 switch (sa->instr) {
22 case SIGMA_ACTION_WRITEXBYTES:
23 case SIGMA_ACTION_WRITESINGLE:
24 case SIGMA_ACTION_WRITESAFELOAD:
25 payload = sigma_action_len(sa);
26 break;
27 default:
28 break;
29 }
30
31 payload = ALIGN(payload, 2);
32
33 return payload + sizeof(struct sigma_action);
34}
35
36/*
37 * Returns a negative error value in case of an error, 0 if processing of
38 * the firmware should be stopped after this action, 1 otherwise.
39 */
18static int 40static int
19process_sigma_action(struct i2c_client *client, struct sigma_firmware *ssfw) 41process_sigma_action(struct i2c_client *client, struct sigma_action *sa)
20{ 42{
21 struct sigma_action *sa = (void *)(ssfw->fw->data + ssfw->pos);
22 size_t len = sigma_action_len(sa); 43 size_t len = sigma_action_len(sa);
23 int ret = 0; 44 int ret;
24 45
25 pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__, 46 pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__,
26 sa->instr, sa->addr, len); 47 sa->instr, sa->addr, len);
@@ -29,44 +50,50 @@ process_sigma_action(struct i2c_client *client, struct sigma_firmware *ssfw)
29 case SIGMA_ACTION_WRITEXBYTES: 50 case SIGMA_ACTION_WRITEXBYTES:
30 case SIGMA_ACTION_WRITESINGLE: 51 case SIGMA_ACTION_WRITESINGLE:
31 case SIGMA_ACTION_WRITESAFELOAD: 52 case SIGMA_ACTION_WRITESAFELOAD:
32 if (ssfw->fw->size < ssfw->pos + len)
33 return -EINVAL;
34 ret = i2c_master_send(client, (void *)&sa->addr, len); 53 ret = i2c_master_send(client, (void *)&sa->addr, len);
35 if (ret < 0) 54 if (ret < 0)
36 return -EINVAL; 55 return -EINVAL;
37 break; 56 break;
38
39 case SIGMA_ACTION_DELAY: 57 case SIGMA_ACTION_DELAY:
40 ret = 0;
41 udelay(len); 58 udelay(len);
42 len = 0; 59 len = 0;
43 break; 60 break;
44
45 case SIGMA_ACTION_END: 61 case SIGMA_ACTION_END:
46 return 1; 62 return 0;
47
48 default: 63 default:
49 return -EINVAL; 64 return -EINVAL;
50 } 65 }
51 66
52 /* when arrive here ret=0 or sent data */ 67 return 1;
53 ssfw->pos += sigma_action_size(sa, len);
54 return ssfw->pos == ssfw->fw->size;
55} 68}
56 69
57static int 70static int
58process_sigma_actions(struct i2c_client *client, struct sigma_firmware *ssfw) 71process_sigma_actions(struct i2c_client *client, struct sigma_firmware *ssfw)
59{ 72{
60 pr_debug("%s: processing %p\n", __func__, ssfw); 73 struct sigma_action *sa;
74 size_t size;
75 int ret;
76
77 while (ssfw->pos + sizeof(*sa) <= ssfw->fw->size) {
78 sa = (struct sigma_action *)(ssfw->fw->data + ssfw->pos);
79
80 size = sigma_action_size(sa);
81 ssfw->pos += size;
82 if (ssfw->pos > ssfw->fw->size || size == 0)
83 break;
84
85 ret = process_sigma_action(client, sa);
61 86
62 while (1) {
63 int ret = process_sigma_action(client, ssfw);
64 pr_debug("%s: action returned %i\n", __func__, ret); 87 pr_debug("%s: action returned %i\n", __func__, ret);
65 if (ret == 1) 88
66 return 0; 89 if (ret <= 0)
67 else if (ret)
68 return ret; 90 return ret;
69 } 91 }
92
93 if (ssfw->pos != ssfw->fw->size)
94 return -EINVAL;
95
96 return 0;
70} 97}
71 98
72int process_sigma_firmware(struct i2c_client *client, const char *name) 99int process_sigma_firmware(struct i2c_client *client, const char *name)
@@ -89,7 +116,14 @@ int process_sigma_firmware(struct i2c_client *client, const char *name)
89 116
90 /* then verify the header */ 117 /* then verify the header */
91 ret = -EINVAL; 118 ret = -EINVAL;
92 if (fw->size < sizeof(*ssfw_head)) 119
120 /*
121 * Reject too small or unreasonable large files. The upper limit has been
122 * chosen a bit arbitrarily, but it should be enough for all practical
123 * purposes and having the limit makes it easier to avoid integer
124 * overflows later in the loading process.
125 */
126 if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000)
93 goto done; 127 goto done;
94 128
95 ssfw_head = (void *)fw->data; 129 ssfw_head = (void *)fw->data;