diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/kprobes.c | 61 |
1 files changed, 51 insertions, 10 deletions
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 456ecedff2d4..334f37472c56 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
| @@ -89,9 +89,10 @@ static int aggr_pre_handler(struct kprobe *p, struct pt_regs *regs) | |||
| 89 | list_for_each_entry(kp, &p->list, list) { | 89 | list_for_each_entry(kp, &p->list, list) { |
| 90 | if (kp->pre_handler) { | 90 | if (kp->pre_handler) { |
| 91 | curr_kprobe = kp; | 91 | curr_kprobe = kp; |
| 92 | kp->pre_handler(kp, regs); | 92 | if (kp->pre_handler(kp, regs)) |
| 93 | curr_kprobe = NULL; | 93 | return 1; |
| 94 | } | 94 | } |
| 95 | curr_kprobe = NULL; | ||
| 95 | } | 96 | } |
| 96 | return 0; | 97 | return 0; |
| 97 | } | 98 | } |
| @@ -125,6 +126,19 @@ static int aggr_fault_handler(struct kprobe *p, struct pt_regs *regs, | |||
| 125 | return 0; | 126 | return 0; |
| 126 | } | 127 | } |
| 127 | 128 | ||
| 129 | static int aggr_break_handler(struct kprobe *p, struct pt_regs *regs) | ||
| 130 | { | ||
| 131 | struct kprobe *kp = curr_kprobe; | ||
| 132 | if (curr_kprobe && kp->break_handler) { | ||
| 133 | if (kp->break_handler(kp, regs)) { | ||
| 134 | curr_kprobe = NULL; | ||
| 135 | return 1; | ||
| 136 | } | ||
| 137 | } | ||
| 138 | curr_kprobe = NULL; | ||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | |||
| 128 | struct kprobe trampoline_p = { | 142 | struct kprobe trampoline_p = { |
| 129 | .addr = (kprobe_opcode_t *) &kretprobe_trampoline, | 143 | .addr = (kprobe_opcode_t *) &kretprobe_trampoline, |
| 130 | .pre_handler = trampoline_probe_handler, | 144 | .pre_handler = trampoline_probe_handler, |
| @@ -258,18 +272,45 @@ static inline void free_rp_inst(struct kretprobe *rp) | |||
| 258 | } | 272 | } |
| 259 | 273 | ||
| 260 | /* | 274 | /* |
| 275 | * Keep all fields in the kprobe consistent | ||
| 276 | */ | ||
| 277 | static inline void copy_kprobe(struct kprobe *old_p, struct kprobe *p) | ||
| 278 | { | ||
| 279 | memcpy(&p->opcode, &old_p->opcode, sizeof(kprobe_opcode_t)); | ||
| 280 | memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_specific_insn)); | ||
| 281 | } | ||
| 282 | |||
| 283 | /* | ||
| 284 | * Add the new probe to old_p->list. Fail if this is the | ||
| 285 | * second jprobe at the address - two jprobes can't coexist | ||
| 286 | */ | ||
| 287 | static int add_new_kprobe(struct kprobe *old_p, struct kprobe *p) | ||
| 288 | { | ||
| 289 | struct kprobe *kp; | ||
| 290 | |||
| 291 | if (p->break_handler) { | ||
| 292 | list_for_each_entry(kp, &old_p->list, list) { | ||
| 293 | if (kp->break_handler) | ||
| 294 | return -EEXIST; | ||
| 295 | } | ||
| 296 | list_add_tail(&p->list, &old_p->list); | ||
| 297 | } else | ||
| 298 | list_add(&p->list, &old_p->list); | ||
| 299 | return 0; | ||
| 300 | } | ||
| 301 | |||
| 302 | /* | ||
| 261 | * Fill in the required fields of the "manager kprobe". Replace the | 303 | * Fill in the required fields of the "manager kprobe". Replace the |
| 262 | * earlier kprobe in the hlist with the manager kprobe | 304 | * earlier kprobe in the hlist with the manager kprobe |
| 263 | */ | 305 | */ |
| 264 | static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p) | 306 | static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p) |
| 265 | { | 307 | { |
| 308 | copy_kprobe(p, ap); | ||
| 266 | ap->addr = p->addr; | 309 | ap->addr = p->addr; |
| 267 | memcpy(&ap->opcode, &p->opcode, sizeof(kprobe_opcode_t)); | ||
| 268 | memcpy(&ap->ainsn, &p->ainsn, sizeof(struct arch_specific_insn)); | ||
| 269 | |||
| 270 | ap->pre_handler = aggr_pre_handler; | 310 | ap->pre_handler = aggr_pre_handler; |
| 271 | ap->post_handler = aggr_post_handler; | 311 | ap->post_handler = aggr_post_handler; |
| 272 | ap->fault_handler = aggr_fault_handler; | 312 | ap->fault_handler = aggr_fault_handler; |
| 313 | ap->break_handler = aggr_break_handler; | ||
| 273 | 314 | ||
| 274 | INIT_LIST_HEAD(&ap->list); | 315 | INIT_LIST_HEAD(&ap->list); |
| 275 | list_add(&p->list, &ap->list); | 316 | list_add(&p->list, &ap->list); |
| @@ -290,16 +331,16 @@ static int register_aggr_kprobe(struct kprobe *old_p, struct kprobe *p) | |||
| 290 | int ret = 0; | 331 | int ret = 0; |
| 291 | struct kprobe *ap; | 332 | struct kprobe *ap; |
| 292 | 333 | ||
| 293 | if (old_p->break_handler || p->break_handler) { | 334 | if (old_p->pre_handler == aggr_pre_handler) { |
| 294 | ret = -EEXIST; /* kprobe and jprobe can't (yet) coexist */ | 335 | copy_kprobe(old_p, p); |
| 295 | } else if (old_p->pre_handler == aggr_pre_handler) { | 336 | ret = add_new_kprobe(old_p, p); |
| 296 | list_add(&p->list, &old_p->list); | ||
| 297 | } else { | 337 | } else { |
| 298 | ap = kcalloc(1, sizeof(struct kprobe), GFP_ATOMIC); | 338 | ap = kcalloc(1, sizeof(struct kprobe), GFP_ATOMIC); |
| 299 | if (!ap) | 339 | if (!ap) |
| 300 | return -ENOMEM; | 340 | return -ENOMEM; |
| 301 | add_aggr_kprobe(ap, old_p); | 341 | add_aggr_kprobe(ap, old_p); |
| 302 | list_add(&p->list, &ap->list); | 342 | copy_kprobe(ap, p); |
| 343 | ret = add_new_kprobe(ap, p); | ||
| 303 | } | 344 | } |
| 304 | return ret; | 345 | return ret; |
| 305 | } | 346 | } |
