diff options
author | Chris Metcalf <cmetcalf@tilera.com> | 2013-11-14 12:09:21 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-11-14 17:19:20 -0500 |
commit | 1ca1a4cf59ea343a1a70084fe7cc96f37f3cf5b1 (patch) | |
tree | 0f79d843ccee424cba6df72fb9a098602d11af74 | |
parent | 2abc2f070eb30ac8421554a5c32229f8332c6206 (diff) |
connector: improved unaligned access error fix
In af3e095a1fb4, Erik Jacobsen fixed one type of unaligned access
bug for ia64 by converting a 64-bit write to use put_unaligned().
Unfortunately, since gcc will convert a short memset() to a series
of appropriately-aligned stores, the problem is now visible again
on tilegx, where the memset that zeros out proc_event is converted
to three 64-bit stores, causing an unaligned access panic.
A better fix for the original problem is to ensure that proc_event
is aligned to 8 bytes here. We can do that relatively easily by
arranging to start the struct cn_msg aligned to 8 bytes and then
offset by 4 bytes. Doing so means that the immediately following
proc_event structure is then correctly aligned to 8 bytes.
The result is that the memset() stores are now aligned, and as an
added benefit, we can remove the put_unaligned() calls in the code.
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/connector/cn_proc.c | 72 |
1 files changed, 42 insertions, 30 deletions
diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c index c73fc2b74de2..18c5b9b16645 100644 --- a/drivers/connector/cn_proc.c +++ b/drivers/connector/cn_proc.c | |||
@@ -32,11 +32,23 @@ | |||
32 | #include <linux/atomic.h> | 32 | #include <linux/atomic.h> |
33 | #include <linux/pid_namespace.h> | 33 | #include <linux/pid_namespace.h> |
34 | 34 | ||
35 | #include <asm/unaligned.h> | ||
36 | |||
37 | #include <linux/cn_proc.h> | 35 | #include <linux/cn_proc.h> |
38 | 36 | ||
39 | #define CN_PROC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event)) | 37 | /* |
38 | * Size of a cn_msg followed by a proc_event structure. Since the | ||
39 | * sizeof struct cn_msg is a multiple of 4 bytes, but not 8 bytes, we | ||
40 | * add one 4-byte word to the size here, and then start the actual | ||
41 | * cn_msg structure 4 bytes into the stack buffer. The result is that | ||
42 | * the immediately following proc_event structure is aligned to 8 bytes. | ||
43 | */ | ||
44 | #define CN_PROC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event) + 4) | ||
45 | |||
46 | /* See comment above; we test our assumption about sizeof struct cn_msg here. */ | ||
47 | static inline struct cn_msg *buffer_to_cn_msg(__u8 *buffer) | ||
48 | { | ||
49 | BUILD_BUG_ON(sizeof(struct cn_msg) != 20); | ||
50 | return (struct cn_msg *)(buffer + 4); | ||
51 | } | ||
40 | 52 | ||
41 | static atomic_t proc_event_num_listeners = ATOMIC_INIT(0); | 53 | static atomic_t proc_event_num_listeners = ATOMIC_INIT(0); |
42 | static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC }; | 54 | static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC }; |
@@ -56,19 +68,19 @@ void proc_fork_connector(struct task_struct *task) | |||
56 | { | 68 | { |
57 | struct cn_msg *msg; | 69 | struct cn_msg *msg; |
58 | struct proc_event *ev; | 70 | struct proc_event *ev; |
59 | __u8 buffer[CN_PROC_MSG_SIZE]; | 71 | __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); |
60 | struct timespec ts; | 72 | struct timespec ts; |
61 | struct task_struct *parent; | 73 | struct task_struct *parent; |
62 | 74 | ||
63 | if (atomic_read(&proc_event_num_listeners) < 1) | 75 | if (atomic_read(&proc_event_num_listeners) < 1) |
64 | return; | 76 | return; |
65 | 77 | ||
66 | msg = (struct cn_msg *)buffer; | 78 | msg = buffer_to_cn_msg(buffer); |
67 | ev = (struct proc_event *)msg->data; | 79 | ev = (struct proc_event *)msg->data; |
68 | memset(&ev->event_data, 0, sizeof(ev->event_data)); | 80 | memset(&ev->event_data, 0, sizeof(ev->event_data)); |
69 | get_seq(&msg->seq, &ev->cpu); | 81 | get_seq(&msg->seq, &ev->cpu); |
70 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ | 82 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ |
71 | put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); | 83 | ev->timestamp_ns = timespec_to_ns(&ts); |
72 | ev->what = PROC_EVENT_FORK; | 84 | ev->what = PROC_EVENT_FORK; |
73 | rcu_read_lock(); | 85 | rcu_read_lock(); |
74 | parent = rcu_dereference(task->real_parent); | 86 | parent = rcu_dereference(task->real_parent); |
@@ -91,17 +103,17 @@ void proc_exec_connector(struct task_struct *task) | |||
91 | struct cn_msg *msg; | 103 | struct cn_msg *msg; |
92 | struct proc_event *ev; | 104 | struct proc_event *ev; |
93 | struct timespec ts; | 105 | struct timespec ts; |
94 | __u8 buffer[CN_PROC_MSG_SIZE]; | 106 | __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); |
95 | 107 | ||
96 | if (atomic_read(&proc_event_num_listeners) < 1) | 108 | if (atomic_read(&proc_event_num_listeners) < 1) |
97 | return; | 109 | return; |
98 | 110 | ||
99 | msg = (struct cn_msg *)buffer; | 111 | msg = buffer_to_cn_msg(buffer); |
100 | ev = (struct proc_event *)msg->data; | 112 | ev = (struct proc_event *)msg->data; |
101 | memset(&ev->event_data, 0, sizeof(ev->event_data)); | 113 | memset(&ev->event_data, 0, sizeof(ev->event_data)); |
102 | get_seq(&msg->seq, &ev->cpu); | 114 | get_seq(&msg->seq, &ev->cpu); |
103 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ | 115 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ |
104 | put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); | 116 | ev->timestamp_ns = timespec_to_ns(&ts); |
105 | ev->what = PROC_EVENT_EXEC; | 117 | ev->what = PROC_EVENT_EXEC; |
106 | ev->event_data.exec.process_pid = task->pid; | 118 | ev->event_data.exec.process_pid = task->pid; |
107 | ev->event_data.exec.process_tgid = task->tgid; | 119 | ev->event_data.exec.process_tgid = task->tgid; |
@@ -117,14 +129,14 @@ void proc_id_connector(struct task_struct *task, int which_id) | |||
117 | { | 129 | { |
118 | struct cn_msg *msg; | 130 | struct cn_msg *msg; |
119 | struct proc_event *ev; | 131 | struct proc_event *ev; |
120 | __u8 buffer[CN_PROC_MSG_SIZE]; | 132 | __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); |
121 | struct timespec ts; | 133 | struct timespec ts; |
122 | const struct cred *cred; | 134 | const struct cred *cred; |
123 | 135 | ||
124 | if (atomic_read(&proc_event_num_listeners) < 1) | 136 | if (atomic_read(&proc_event_num_listeners) < 1) |
125 | return; | 137 | return; |
126 | 138 | ||
127 | msg = (struct cn_msg *)buffer; | 139 | msg = buffer_to_cn_msg(buffer); |
128 | ev = (struct proc_event *)msg->data; | 140 | ev = (struct proc_event *)msg->data; |
129 | memset(&ev->event_data, 0, sizeof(ev->event_data)); | 141 | memset(&ev->event_data, 0, sizeof(ev->event_data)); |
130 | ev->what = which_id; | 142 | ev->what = which_id; |
@@ -145,7 +157,7 @@ void proc_id_connector(struct task_struct *task, int which_id) | |||
145 | rcu_read_unlock(); | 157 | rcu_read_unlock(); |
146 | get_seq(&msg->seq, &ev->cpu); | 158 | get_seq(&msg->seq, &ev->cpu); |
147 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ | 159 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ |
148 | put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); | 160 | ev->timestamp_ns = timespec_to_ns(&ts); |
149 | 161 | ||
150 | memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); | 162 | memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id)); |
151 | msg->ack = 0; /* not used */ | 163 | msg->ack = 0; /* not used */ |
@@ -159,17 +171,17 @@ void proc_sid_connector(struct task_struct *task) | |||
159 | struct cn_msg *msg; | 171 | struct cn_msg *msg; |
160 | struct proc_event *ev; | 172 | struct proc_event *ev; |
161 | struct timespec ts; | 173 | struct timespec ts; |
162 | __u8 buffer[CN_PROC_MSG_SIZE]; | 174 | __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); |
163 | 175 | ||
164 | if (atomic_read(&proc_event_num_listeners) < 1) | 176 | if (atomic_read(&proc_event_num_listeners) < 1) |
165 | return; | 177 | return; |
166 | 178 | ||
167 | msg = (struct cn_msg *)buffer; | 179 | msg = buffer_to_cn_msg(buffer); |
168 | ev = (struct proc_event *)msg->data; | 180 | ev = (struct proc_event *)msg->data; |
169 | memset(&ev->event_data, 0, sizeof(ev->event_data)); | 181 | memset(&ev->event_data, 0, sizeof(ev->event_data)); |
170 | get_seq(&msg->seq, &ev->cpu); | 182 | get_seq(&msg->seq, &ev->cpu); |
171 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ | 183 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ |
172 | put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); | 184 | ev->timestamp_ns = timespec_to_ns(&ts); |
173 | ev->what = PROC_EVENT_SID; | 185 | ev->what = PROC_EVENT_SID; |
174 | ev->event_data.sid.process_pid = task->pid; | 186 | ev->event_data.sid.process_pid = task->pid; |
175 | ev->event_data.sid.process_tgid = task->tgid; | 187 | ev->event_data.sid.process_tgid = task->tgid; |
@@ -186,17 +198,17 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id) | |||
186 | struct cn_msg *msg; | 198 | struct cn_msg *msg; |
187 | struct proc_event *ev; | 199 | struct proc_event *ev; |
188 | struct timespec ts; | 200 | struct timespec ts; |
189 | __u8 buffer[CN_PROC_MSG_SIZE]; | 201 | __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); |
190 | 202 | ||
191 | if (atomic_read(&proc_event_num_listeners) < 1) | 203 | if (atomic_read(&proc_event_num_listeners) < 1) |
192 | return; | 204 | return; |
193 | 205 | ||
194 | msg = (struct cn_msg *)buffer; | 206 | msg = buffer_to_cn_msg(buffer); |
195 | ev = (struct proc_event *)msg->data; | 207 | ev = (struct proc_event *)msg->data; |
196 | memset(&ev->event_data, 0, sizeof(ev->event_data)); | 208 | memset(&ev->event_data, 0, sizeof(ev->event_data)); |
197 | get_seq(&msg->seq, &ev->cpu); | 209 | get_seq(&msg->seq, &ev->cpu); |
198 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ | 210 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ |
199 | put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); | 211 | ev->timestamp_ns = timespec_to_ns(&ts); |
200 | ev->what = PROC_EVENT_PTRACE; | 212 | ev->what = PROC_EVENT_PTRACE; |
201 | ev->event_data.ptrace.process_pid = task->pid; | 213 | ev->event_data.ptrace.process_pid = task->pid; |
202 | ev->event_data.ptrace.process_tgid = task->tgid; | 214 | ev->event_data.ptrace.process_tgid = task->tgid; |
@@ -221,17 +233,17 @@ void proc_comm_connector(struct task_struct *task) | |||
221 | struct cn_msg *msg; | 233 | struct cn_msg *msg; |
222 | struct proc_event *ev; | 234 | struct proc_event *ev; |
223 | struct timespec ts; | 235 | struct timespec ts; |
224 | __u8 buffer[CN_PROC_MSG_SIZE]; | 236 | __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); |
225 | 237 | ||
226 | if (atomic_read(&proc_event_num_listeners) < 1) | 238 | if (atomic_read(&proc_event_num_listeners) < 1) |
227 | return; | 239 | return; |
228 | 240 | ||
229 | msg = (struct cn_msg *)buffer; | 241 | msg = buffer_to_cn_msg(buffer); |
230 | ev = (struct proc_event *)msg->data; | 242 | ev = (struct proc_event *)msg->data; |
231 | memset(&ev->event_data, 0, sizeof(ev->event_data)); | 243 | memset(&ev->event_data, 0, sizeof(ev->event_data)); |
232 | get_seq(&msg->seq, &ev->cpu); | 244 | get_seq(&msg->seq, &ev->cpu); |
233 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ | 245 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ |
234 | put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); | 246 | ev->timestamp_ns = timespec_to_ns(&ts); |
235 | ev->what = PROC_EVENT_COMM; | 247 | ev->what = PROC_EVENT_COMM; |
236 | ev->event_data.comm.process_pid = task->pid; | 248 | ev->event_data.comm.process_pid = task->pid; |
237 | ev->event_data.comm.process_tgid = task->tgid; | 249 | ev->event_data.comm.process_tgid = task->tgid; |
@@ -248,18 +260,18 @@ void proc_coredump_connector(struct task_struct *task) | |||
248 | { | 260 | { |
249 | struct cn_msg *msg; | 261 | struct cn_msg *msg; |
250 | struct proc_event *ev; | 262 | struct proc_event *ev; |
251 | __u8 buffer[CN_PROC_MSG_SIZE]; | 263 | __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); |
252 | struct timespec ts; | 264 | struct timespec ts; |
253 | 265 | ||
254 | if (atomic_read(&proc_event_num_listeners) < 1) | 266 | if (atomic_read(&proc_event_num_listeners) < 1) |
255 | return; | 267 | return; |
256 | 268 | ||
257 | msg = (struct cn_msg *)buffer; | 269 | msg = buffer_to_cn_msg(buffer); |
258 | ev = (struct proc_event *)msg->data; | 270 | ev = (struct proc_event *)msg->data; |
259 | memset(&ev->event_data, 0, sizeof(ev->event_data)); | 271 | memset(&ev->event_data, 0, sizeof(ev->event_data)); |
260 | get_seq(&msg->seq, &ev->cpu); | 272 | get_seq(&msg->seq, &ev->cpu); |
261 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ | 273 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ |
262 | put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); | 274 | ev->timestamp_ns = timespec_to_ns(&ts); |
263 | ev->what = PROC_EVENT_COREDUMP; | 275 | ev->what = PROC_EVENT_COREDUMP; |
264 | ev->event_data.coredump.process_pid = task->pid; | 276 | ev->event_data.coredump.process_pid = task->pid; |
265 | ev->event_data.coredump.process_tgid = task->tgid; | 277 | ev->event_data.coredump.process_tgid = task->tgid; |
@@ -275,18 +287,18 @@ void proc_exit_connector(struct task_struct *task) | |||
275 | { | 287 | { |
276 | struct cn_msg *msg; | 288 | struct cn_msg *msg; |
277 | struct proc_event *ev; | 289 | struct proc_event *ev; |
278 | __u8 buffer[CN_PROC_MSG_SIZE]; | 290 | __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); |
279 | struct timespec ts; | 291 | struct timespec ts; |
280 | 292 | ||
281 | if (atomic_read(&proc_event_num_listeners) < 1) | 293 | if (atomic_read(&proc_event_num_listeners) < 1) |
282 | return; | 294 | return; |
283 | 295 | ||
284 | msg = (struct cn_msg *)buffer; | 296 | msg = buffer_to_cn_msg(buffer); |
285 | ev = (struct proc_event *)msg->data; | 297 | ev = (struct proc_event *)msg->data; |
286 | memset(&ev->event_data, 0, sizeof(ev->event_data)); | 298 | memset(&ev->event_data, 0, sizeof(ev->event_data)); |
287 | get_seq(&msg->seq, &ev->cpu); | 299 | get_seq(&msg->seq, &ev->cpu); |
288 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ | 300 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ |
289 | put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); | 301 | ev->timestamp_ns = timespec_to_ns(&ts); |
290 | ev->what = PROC_EVENT_EXIT; | 302 | ev->what = PROC_EVENT_EXIT; |
291 | ev->event_data.exit.process_pid = task->pid; | 303 | ev->event_data.exit.process_pid = task->pid; |
292 | ev->event_data.exit.process_tgid = task->tgid; | 304 | ev->event_data.exit.process_tgid = task->tgid; |
@@ -312,18 +324,18 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack) | |||
312 | { | 324 | { |
313 | struct cn_msg *msg; | 325 | struct cn_msg *msg; |
314 | struct proc_event *ev; | 326 | struct proc_event *ev; |
315 | __u8 buffer[CN_PROC_MSG_SIZE]; | 327 | __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8); |
316 | struct timespec ts; | 328 | struct timespec ts; |
317 | 329 | ||
318 | if (atomic_read(&proc_event_num_listeners) < 1) | 330 | if (atomic_read(&proc_event_num_listeners) < 1) |
319 | return; | 331 | return; |
320 | 332 | ||
321 | msg = (struct cn_msg *)buffer; | 333 | msg = buffer_to_cn_msg(buffer); |
322 | ev = (struct proc_event *)msg->data; | 334 | ev = (struct proc_event *)msg->data; |
323 | memset(&ev->event_data, 0, sizeof(ev->event_data)); | 335 | memset(&ev->event_data, 0, sizeof(ev->event_data)); |
324 | msg->seq = rcvd_seq; | 336 | msg->seq = rcvd_seq; |
325 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ | 337 | ktime_get_ts(&ts); /* get high res monotonic timestamp */ |
326 | put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns); | 338 | ev->timestamp_ns = timespec_to_ns(&ts); |
327 | ev->cpu = -1; | 339 | ev->cpu = -1; |
328 | ev->what = PROC_EVENT_NONE; | 340 | ev->what = PROC_EVENT_NONE; |
329 | ev->event_data.ack.err = err; | 341 | ev->event_data.ack.err = err; |