diff options
author | Oleg Nesterov <oleg@redhat.com> | 2012-09-23 15:30:44 -0400 |
---|---|---|
committer | Oleg Nesterov <oleg@redhat.com> | 2012-09-29 15:21:54 -0400 |
commit | ed6f6a50dc5f183c53e7b3b7fed4794bc50d9aa7 (patch) | |
tree | 8ef3472db708804bb292a79752d0b04de8f2060d /kernel/events | |
parent | cceb55aab73d2aea8f4d6f7414d2e1b647a3dacb (diff) |
uprobes: Kill set_orig_insn()->is_swbp_at_addr()
Unlike set_swbp(), set_orig_insn()->is_swbp_at_addr() makes sense,
although it can't prevent all confusions.
But the usage of is_swbp_at_addr() is equally confusing, and it adds
the extra get_user_pages() we can avoid.
This patch removes set_orig_insn()->is_swbp_at_addr() but changes
write_opcode() to do the necessary checks before replace_page().
Perhaps it also makes sense to ensure PAGE_MAPPING_ANON in unregister
case.
find_active_uprobe() becomes the only user of is_swbp_at_addr(),
we can change its semantics.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Diffstat (limited to 'kernel/events')
-rw-r--r-- | kernel/events/uprobes.c | 32 |
1 files changed, 23 insertions, 9 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index b6f0f716a884..9248ee76b4bb 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c | |||
@@ -190,6 +190,25 @@ static void copy_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t | |||
190 | kunmap_atomic(kaddr); | 190 | kunmap_atomic(kaddr); |
191 | } | 191 | } |
192 | 192 | ||
193 | static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t *new_opcode) | ||
194 | { | ||
195 | uprobe_opcode_t old_opcode; | ||
196 | bool is_swbp; | ||
197 | |||
198 | copy_opcode(page, vaddr, &old_opcode); | ||
199 | is_swbp = is_swbp_insn(&old_opcode); | ||
200 | |||
201 | if (is_swbp_insn(new_opcode)) { | ||
202 | if (is_swbp) /* register: already installed? */ | ||
203 | return 0; | ||
204 | } else { | ||
205 | if (!is_swbp) /* unregister: was it changed by us? */ | ||
206 | return -EINVAL; | ||
207 | } | ||
208 | |||
209 | return 1; | ||
210 | } | ||
211 | |||
193 | /* | 212 | /* |
194 | * NOTE: | 213 | * NOTE: |
195 | * Expect the breakpoint instruction to be the smallest size instruction for | 214 | * Expect the breakpoint instruction to be the smallest size instruction for |
@@ -226,6 +245,10 @@ retry: | |||
226 | if (ret <= 0) | 245 | if (ret <= 0) |
227 | return ret; | 246 | return ret; |
228 | 247 | ||
248 | ret = verify_opcode(old_page, vaddr, &opcode); | ||
249 | if (ret <= 0) | ||
250 | goto put_old; | ||
251 | |||
229 | ret = -ENOMEM; | 252 | ret = -ENOMEM; |
230 | new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); | 253 | new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); |
231 | if (!new_page) | 254 | if (!new_page) |
@@ -311,15 +334,6 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned | |||
311 | int __weak | 334 | int __weak |
312 | set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) | 335 | set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) |
313 | { | 336 | { |
314 | int result; | ||
315 | |||
316 | result = is_swbp_at_addr(mm, vaddr); | ||
317 | if (!result) | ||
318 | return -EINVAL; | ||
319 | |||
320 | if (result != 1) | ||
321 | return result; | ||
322 | |||
323 | return write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn); | 337 | return write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn); |
324 | } | 338 | } |
325 | 339 | ||