diff options
author | Steven Rostedt <srostedt@redhat.com> | 2008-11-25 09:39:18 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-11-28 08:07:50 -0500 |
commit | d9af12b72bfe2a4efc1d347e0ac1c669b85dcea9 (patch) | |
tree | 19e7d7ccc0c96d3bc29468bca03d8a98762d44c1 /arch/powerpc/kernel/ftrace.c | |
parent | c7b0d17366d6e04a11470fc8d85f9fbac02671b9 (diff) |
powerpc: ftrace, fix cast aliasing and add code verification
Impact: clean up and robustness addition
This patch addresses the comments made by Paul Mackerras.
It removes the type casting between unsigned int and unsigned char
pointers, and replaces them with a use of all unsigned int.
Verification that the jump is indeed made to a trampoline has also
been added.
Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/powerpc/kernel/ftrace.c')
-rw-r--r-- | arch/powerpc/kernel/ftrace.c | 121 |
1 files changed, 65 insertions, 56 deletions
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c index 3271cd698e4c..ea454a004406 100644 --- a/arch/powerpc/kernel/ftrace.c +++ b/arch/powerpc/kernel/ftrace.c | |||
@@ -162,26 +162,25 @@ static int | |||
162 | __ftrace_make_nop(struct module *mod, | 162 | __ftrace_make_nop(struct module *mod, |
163 | struct dyn_ftrace *rec, unsigned long addr) | 163 | struct dyn_ftrace *rec, unsigned long addr) |
164 | { | 164 | { |
165 | unsigned char replaced[MCOUNT_INSN_SIZE * 2]; | 165 | unsigned int op; |
166 | unsigned int *op = (unsigned *)&replaced; | 166 | unsigned int jmp[5]; |
167 | unsigned char jmp[8]; | 167 | unsigned long ptr; |
168 | unsigned long *ptr = (unsigned long *)&jmp; | ||
169 | unsigned long ip = rec->ip; | 168 | unsigned long ip = rec->ip; |
170 | unsigned long tramp; | 169 | unsigned long tramp; |
171 | int offset; | 170 | int offset; |
172 | 171 | ||
173 | /* read where this goes */ | 172 | /* read where this goes */ |
174 | if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) | 173 | if (probe_kernel_read(&op, (void *)ip, sizeof(int))) |
175 | return -EFAULT; | 174 | return -EFAULT; |
176 | 175 | ||
177 | /* Make sure that that this is still a 24bit jump */ | 176 | /* Make sure that that this is still a 24bit jump */ |
178 | if (!is_bl_op(*op)) { | 177 | if (!is_bl_op(op)) { |
179 | printk(KERN_ERR "Not expected bl: opcode is %x\n", *op); | 178 | printk(KERN_ERR "Not expected bl: opcode is %x\n", op); |
180 | return -EINVAL; | 179 | return -EINVAL; |
181 | } | 180 | } |
182 | 181 | ||
183 | /* lets find where the pointer goes */ | 182 | /* lets find where the pointer goes */ |
184 | tramp = find_bl_target(ip, *op); | 183 | tramp = find_bl_target(ip, op); |
185 | 184 | ||
186 | /* | 185 | /* |
187 | * On PPC64 the trampoline looks like: | 186 | * On PPC64 the trampoline looks like: |
@@ -200,19 +199,25 @@ __ftrace_make_nop(struct module *mod, | |||
200 | DEBUGP("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc); | 199 | DEBUGP("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc); |
201 | 200 | ||
202 | /* Find where the trampoline jumps to */ | 201 | /* Find where the trampoline jumps to */ |
203 | if (probe_kernel_read(jmp, (void *)tramp, 8)) { | 202 | if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) { |
204 | printk(KERN_ERR "Failed to read %lx\n", tramp); | 203 | printk(KERN_ERR "Failed to read %lx\n", tramp); |
205 | return -EFAULT; | 204 | return -EFAULT; |
206 | } | 205 | } |
207 | 206 | ||
208 | DEBUGP(" %08x %08x", | 207 | DEBUGP(" %08x %08x", jmp[0], jmp[1]); |
209 | (unsigned)(*ptr >> 32), | 208 | |
210 | (unsigned)*ptr); | 209 | /* verify that this is what we expect it to be */ |
210 | if (((jmp[0] & 0xffff0000) != 0x3d820000) || | ||
211 | ((jmp[1] & 0xffff0000) != 0x398c0000) || | ||
212 | (jmp[2] != 0xf8410028) || | ||
213 | (jmp[3] != 0xe96c0020) || | ||
214 | (jmp[4] != 0xe84c0028)) { | ||
215 | printk(KERN_ERR "Not a trampoline\n"); | ||
216 | return -EINVAL; | ||
217 | } | ||
211 | 218 | ||
212 | offset = (unsigned)jmp[2] << 24 | | 219 | offset = (unsigned)((unsigned short)jmp[0]) << 16 | |
213 | (unsigned)jmp[3] << 16 | | 220 | (unsigned)((unsigned short)jmp[1]); |
214 | (unsigned)jmp[6] << 8 | | ||
215 | (unsigned)jmp[7]; | ||
216 | 221 | ||
217 | DEBUGP(" %x ", offset); | 222 | DEBUGP(" %x ", offset); |
218 | 223 | ||
@@ -225,13 +230,13 @@ __ftrace_make_nop(struct module *mod, | |||
225 | return -EFAULT; | 230 | return -EFAULT; |
226 | } | 231 | } |
227 | 232 | ||
228 | DEBUGP(" %08x %08x\n", | 233 | DEBUGP(" %08x %08x\n", jmp[0], jmp[1]); |
229 | (unsigned)(*ptr >> 32), | 234 | |
230 | (unsigned)*ptr); | 235 | ptr = ((unsigned long)jmp[0] << 32) + jmp[1]; |
231 | 236 | ||
232 | /* This should match what was called */ | 237 | /* This should match what was called */ |
233 | if (*ptr != GET_ADDR(addr)) { | 238 | if (ptr != GET_ADDR(addr)) { |
234 | printk(KERN_ERR "addr does not match %lx\n", *ptr); | 239 | printk(KERN_ERR "addr does not match %lx\n", ptr); |
235 | return -EINVAL; | 240 | return -EINVAL; |
236 | } | 241 | } |
237 | 242 | ||
@@ -240,11 +245,11 @@ __ftrace_make_nop(struct module *mod, | |||
240 | * 0xe8, 0x41, 0x00, 0x28 ld r2,40(r1) | 245 | * 0xe8, 0x41, 0x00, 0x28 ld r2,40(r1) |
241 | * This needs to be turned to a nop too. | 246 | * This needs to be turned to a nop too. |
242 | */ | 247 | */ |
243 | if (probe_kernel_read(replaced, (void *)(ip+4), MCOUNT_INSN_SIZE)) | 248 | if (probe_kernel_read(&op, (void *)(ip+4), MCOUNT_INSN_SIZE)) |
244 | return -EFAULT; | 249 | return -EFAULT; |
245 | 250 | ||
246 | if (*op != 0xe8410028) { | 251 | if (op != 0xe8410028) { |
247 | printk(KERN_ERR "Next line is not ld! (%08x)\n", *op); | 252 | printk(KERN_ERR "Next line is not ld! (%08x)\n", op); |
248 | return -EINVAL; | 253 | return -EINVAL; |
249 | } | 254 | } |
250 | 255 | ||
@@ -261,9 +266,9 @@ __ftrace_make_nop(struct module *mod, | |||
261 | * ld r2,40(r1) | 266 | * ld r2,40(r1) |
262 | * 1: | 267 | * 1: |
263 | */ | 268 | */ |
264 | op[0] = 0x48000008; /* b +8 */ | 269 | op = 0x48000008; /* b +8 */ |
265 | 270 | ||
266 | if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE)) | 271 | if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE)) |
267 | return -EPERM; | 272 | return -EPERM; |
268 | 273 | ||
269 | return 0; | 274 | return 0; |
@@ -274,46 +279,52 @@ static int | |||
274 | __ftrace_make_nop(struct module *mod, | 279 | __ftrace_make_nop(struct module *mod, |
275 | struct dyn_ftrace *rec, unsigned long addr) | 280 | struct dyn_ftrace *rec, unsigned long addr) |
276 | { | 281 | { |
277 | unsigned char replaced[MCOUNT_INSN_SIZE]; | 282 | unsigned int op; |
278 | unsigned int *op = (unsigned *)&replaced; | 283 | unsigned int jmp[4]; |
279 | unsigned char jmp[8]; | ||
280 | unsigned int *ptr = (unsigned int *)&jmp; | ||
281 | unsigned long ip = rec->ip; | 284 | unsigned long ip = rec->ip; |
282 | unsigned long tramp; | 285 | unsigned long tramp; |
283 | int offset; | ||
284 | 286 | ||
285 | if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) | 287 | if (probe_kernel_read(&op, (void *)ip, MCOUNT_INSN_SIZE)) |
286 | return -EFAULT; | 288 | return -EFAULT; |
287 | 289 | ||
288 | /* Make sure that that this is still a 24bit jump */ | 290 | /* Make sure that that this is still a 24bit jump */ |
289 | if (!is_bl_op(*op)) { | 291 | if (!is_bl_op(op)) { |
290 | printk(KERN_ERR "Not expected bl: opcode is %x\n", *op); | 292 | printk(KERN_ERR "Not expected bl: opcode is %x\n", op); |
291 | return -EINVAL; | 293 | return -EINVAL; |
292 | } | 294 | } |
293 | 295 | ||
294 | /* lets find where the pointer goes */ | 296 | /* lets find where the pointer goes */ |
295 | tramp = find_bl_target(ip, *op); | 297 | tramp = find_bl_target(ip, op); |
296 | 298 | ||
297 | /* | 299 | /* |
298 | * On PPC32 the trampoline looks like: | 300 | * On PPC32 the trampoline looks like: |
299 | * lis r11,sym@ha | 301 | * 0x3d, 0x60, 0x00, 0x00 lis r11,sym@ha |
300 | * addi r11,r11,sym@l | 302 | * 0x39, 0x6b, 0x00, 0x00 addi r11,r11,sym@l |
301 | * mtctr r11 | 303 | * 0x7d, 0x69, 0x03, 0xa6 mtctr r11 |
302 | * bctr | 304 | * 0x4e, 0x80, 0x04, 0x20 bctr |
303 | */ | 305 | */ |
304 | 306 | ||
305 | DEBUGP("ip:%lx jumps to %lx", ip, tramp); | 307 | DEBUGP("ip:%lx jumps to %lx", ip, tramp); |
306 | 308 | ||
307 | /* Find where the trampoline jumps to */ | 309 | /* Find where the trampoline jumps to */ |
308 | if (probe_kernel_read(jmp, (void *)tramp, 8)) { | 310 | if (probe_kernel_read(jmp, (void *)tramp, sizeof(jmp))) { |
309 | printk(KERN_ERR "Failed to read %lx\n", tramp); | 311 | printk(KERN_ERR "Failed to read %lx\n", tramp); |
310 | return -EFAULT; | 312 | return -EFAULT; |
311 | } | 313 | } |
312 | 314 | ||
313 | DEBUGP(" %08x %08x ", ptr[0], ptr[1]); | 315 | DEBUGP(" %08x %08x ", jmp[0], jmp[1]); |
316 | |||
317 | /* verify that this is what we expect it to be */ | ||
318 | if (((jmp[0] & 0xffff0000) != 0x3d600000) || | ||
319 | ((jmp[1] & 0xffff0000) != 0x396b0000) || | ||
320 | (jmp[2] != 0x7d6903a6) || | ||
321 | (jmp[3] != 0x4e800420)) { | ||
322 | printk(KERN_ERR "Not a trampoline\n"); | ||
323 | return -EINVAL; | ||
324 | } | ||
314 | 325 | ||
315 | tramp = (ptr[1] & 0xffff) | | 326 | tramp = (jmp[1] & 0xffff) | |
316 | ((ptr[0] & 0xffff) << 16); | 327 | ((jmp[0] & 0xffff) << 16); |
317 | if (tramp & 0x8000) | 328 | if (tramp & 0x8000) |
318 | tramp -= 0x10000; | 329 | tramp -= 0x10000; |
319 | 330 | ||
@@ -326,9 +337,9 @@ __ftrace_make_nop(struct module *mod, | |||
326 | return -EINVAL; | 337 | return -EINVAL; |
327 | } | 338 | } |
328 | 339 | ||
329 | op[0] = PPC_NOP_INSTR; | 340 | op = PPC_NOP_INSTR; |
330 | 341 | ||
331 | if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE)) | 342 | if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE)) |
332 | return -EPERM; | 343 | return -EPERM; |
333 | 344 | ||
334 | return 0; | 345 | return 0; |
@@ -384,13 +395,12 @@ int ftrace_make_nop(struct module *mod, | |||
384 | static int | 395 | static int |
385 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | 396 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) |
386 | { | 397 | { |
387 | unsigned char replaced[MCOUNT_INSN_SIZE * 2]; | 398 | unsigned int op[2]; |
388 | unsigned int *op = (unsigned *)&replaced; | ||
389 | unsigned long ip = rec->ip; | 399 | unsigned long ip = rec->ip; |
390 | unsigned long offset; | 400 | unsigned long offset; |
391 | 401 | ||
392 | /* read where this goes */ | 402 | /* read where this goes */ |
393 | if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE * 2)) | 403 | if (probe_kernel_read(op, (void *)ip, MCOUNT_INSN_SIZE * 2)) |
394 | return -EFAULT; | 404 | return -EFAULT; |
395 | 405 | ||
396 | /* | 406 | /* |
@@ -425,7 +435,7 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
425 | 435 | ||
426 | DEBUGP("write to %lx\n", rec->ip); | 436 | DEBUGP("write to %lx\n", rec->ip); |
427 | 437 | ||
428 | if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE * 2)) | 438 | if (probe_kernel_write((void *)ip, op, MCOUNT_INSN_SIZE * 2)) |
429 | return -EPERM; | 439 | return -EPERM; |
430 | 440 | ||
431 | return 0; | 441 | return 0; |
@@ -434,18 +444,17 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
434 | static int | 444 | static int |
435 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | 445 | __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) |
436 | { | 446 | { |
437 | unsigned char replaced[MCOUNT_INSN_SIZE]; | 447 | unsigned int op; |
438 | unsigned int *op = (unsigned *)&replaced; | ||
439 | unsigned long ip = rec->ip; | 448 | unsigned long ip = rec->ip; |
440 | unsigned long offset; | 449 | unsigned long offset; |
441 | 450 | ||
442 | /* read where this goes */ | 451 | /* read where this goes */ |
443 | if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE)) | 452 | if (probe_kernel_read(&op, (void *)ip, MCOUNT_INSN_SIZE)) |
444 | return -EFAULT; | 453 | return -EFAULT; |
445 | 454 | ||
446 | /* It should be pointing to a nop */ | 455 | /* It should be pointing to a nop */ |
447 | if (op[0] != PPC_NOP_INSTR) { | 456 | if (op != PPC_NOP_INSTR) { |
448 | printk(KERN_ERR "Expected NOP but have %x\n", op[0]); | 457 | printk(KERN_ERR "Expected NOP but have %x\n", op); |
449 | return -EINVAL; | 458 | return -EINVAL; |
450 | } | 459 | } |
451 | 460 | ||
@@ -465,11 +474,11 @@ __ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
465 | } | 474 | } |
466 | 475 | ||
467 | /* Set to "bl addr" */ | 476 | /* Set to "bl addr" */ |
468 | op[0] = branch_offset(offset); | 477 | op = branch_offset(offset); |
469 | 478 | ||
470 | DEBUGP("write to %lx\n", rec->ip); | 479 | DEBUGP("write to %lx\n", rec->ip); |
471 | 480 | ||
472 | if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE)) | 481 | if (probe_kernel_write((void *)ip, &op, MCOUNT_INSN_SIZE)) |
473 | return -EPERM; | 482 | return -EPERM; |
474 | 483 | ||
475 | return 0; | 484 | return 0; |