History

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.

Lars-Peter Clausen committed 14 years ago

Commit 4f718a21 parent fc8e6e82 files · +55 −26Browse files

Unified · Split

2 files
  1. drivers/firmware/sigma.c +55 −21
  2. include/linux/sigma.h +0 −5
drivers/firmware/sigma.c+55−21
@@ -14,13 +14,34 @@ · 13 unmodified lines
1414 #include <linux/module.h>
1515 #include <linux/sigma.h>
1616
17−/* Return: 0==OK, <0==error, =1 ==no more actions */
17+static 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+ */
1840 static int
19−process_sigma_action(struct i2c_client *client, struct sigma_firmware *ssfw)
41+process_sigma_action(struct i2c_client *client, struct sigma_action *sa)
2042 {
21− struct sigma_action *sa = (void *)(ssfw->fw->data + ssfw->pos);
2243 size_t len = sigma_action_len(sa);
23− int ret = 0;
44+ int ret;
2445
2546 pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__,
2647 sa->instr, sa->addr, len);
@@ -29,44 +50,50 @@ process_sigma_action(struct i2c_client *client, struct sigma_firmware *ssfw) · 2 unmodified lines
2950 case SIGMA_ACTION_WRITEXBYTES:
3051 case SIGMA_ACTION_WRITESINGLE:
3152 case SIGMA_ACTION_WRITESAFELOAD:
32− if (ssfw->fw->size < ssfw->pos + len)
33− return -EINVAL;
3453 ret = i2c_master_send(client, (void *)&sa->addr, len);
3554 if (ret < 0)
3655 return -EINVAL;
3756 break;
38−
3957 case SIGMA_ACTION_DELAY:
40− ret = 0;
4158 udelay(len);
4259 len = 0;
4360 break;
44−
4561 case SIGMA_ACTION_END:
46− return 1;
47−
62+ return 0;
4863 default:
4964 return -EINVAL;
5065 }
5166
52− /* when arrive here ret=0 or sent data */
53− ssfw->pos += sigma_action_size(sa, len);
54− return ssfw->pos == ssfw->fw->size;
67+ return 1;
5568 }
5669
5770 static int
5871 process_sigma_actions(struct i2c_client *client, struct sigma_firmware *ssfw)
5972 {
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);
6186
62− while (1) {
63− int ret = process_sigma_action(client, ssfw);
6487 pr_debug("%s: action returned %i\n", __func__, ret);
65− if (ret == 1)
66− return 0;
67− else if (ret)
88+
89+ if (ret <= 0)
6890 return ret;
6991 }
92+
93+ if (ssfw->pos != ssfw->fw->size)
94+ return -EINVAL;
95+
96+ return 0;
7097 }
7198
7299 int 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) · 16 unmodified lines
89116
90117 /* then verify the header */
91118 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)
93127 goto done;
94128
95129 ssfw_head = (void *)fw->data;
include/linux/sigma.h+0−5
@@ -50,11 +50,6 @@ static inline u32 sigma_action_len(struct sigma_action *sa) · 49 unmodified lines
5050 return (sa->len_hi << 16) | sa->len;
5151 }
5252
53−static inline size_t sigma_action_size(struct sigma_action *sa, u32 payload_len)
54−{
55− return sizeof(*sa) + payload_len + (payload_len % 2);
56−}
57−
5853 extern int process_sigma_firmware(struct i2c_client *client, const char *name);
5954
6055 #endif

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t