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
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 int40static 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) · 2 unmodified lines
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 int70static 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) · 16 unmodified lines
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;
include/linux/sigma.h+0−5
@@ -50,11 +50,6 @@ static inline u32 sigma_action_len(struct sigma_action *sa) · 49 unmodified lines
50 return (sa->len_hi << 16) | sa->len;50 return (sa->len_hi << 16) | sa->len;
51}51}
52 52
53static 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
58extern int process_sigma_firmware(struct i2c_client *client, const char *name);53extern int process_sigma_firmware(struct i2c_client *client, const char *name);
59 54
60#endif55#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