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 | |
| 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')
| -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 | ||
