aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2008-11-25 09:39:18 -0500
committerIngo Molnar <mingo@elte.hu>2008-11-28 08:07:50 -0500
commitd9af12b72bfe2a4efc1d347e0ac1c669b85dcea9 (patch)
tree19e7d7ccc0c96d3bc29468bca03d8a98762d44c1
parentc7b0d17366d6e04a11470fc8d85f9fbac02671b9 (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>
-rw-r--r--arch/powerpc/kernel/ftrace.c121
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,
384static int 395static 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)
434static int 444static 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;