diff options
Diffstat (limited to 'kernel/kprobes.c')
-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 | } |