aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2012-10-21 12:18:17 -0400
committerIngo Molnar <mingo@kernel.org>2012-10-21 12:18:17 -0400
commitf38787f4f921222d080d976ef59210ce3c6c6cb4 (patch)
tree55e643bc1e5b066f04323a4054fdc814726221be /kernel
parent957b9095ed296311b36d26e4316d7f637351ac57 (diff)
parent71434f2fcba5c22d6e0d51878ba8e241a5dea5fb (diff)
Merge branch 'uprobes/core' of git://git.kernel.org/pub/scm/linux/kernel/git/oleg/misc into perf/urgent
Pull various uprobes bugfixes from Oleg Nesterov - mostly race and failure path fixes. Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/events/uprobes.c345
1 files changed, 162 insertions, 183 deletions
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 98256bc71ee1..5cc4e7e42e68 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -78,15 +78,23 @@ static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];
78 */ 78 */
79static atomic_t uprobe_events = ATOMIC_INIT(0); 79static atomic_t uprobe_events = ATOMIC_INIT(0);
80 80
81/* Have a copy of original instruction */
82#define UPROBE_COPY_INSN 0
83/* Dont run handlers when first register/ last unregister in progress*/
84#define UPROBE_RUN_HANDLER 1
85/* Can skip singlestep */
86#define UPROBE_SKIP_SSTEP 2
87
81struct uprobe { 88struct uprobe {
82 struct rb_node rb_node; /* node in the rb tree */ 89 struct rb_node rb_node; /* node in the rb tree */
83 atomic_t ref; 90 atomic_t ref;
84 struct rw_semaphore consumer_rwsem; 91 struct rw_semaphore consumer_rwsem;
92 struct mutex copy_mutex; /* TODO: kill me and UPROBE_COPY_INSN */
85 struct list_head pending_list; 93 struct list_head pending_list;
86 struct uprobe_consumer *consumers; 94 struct uprobe_consumer *consumers;
87 struct inode *inode; /* Also hold a ref to inode */ 95 struct inode *inode; /* Also hold a ref to inode */
88 loff_t offset; 96 loff_t offset;
89 int flags; 97 unsigned long flags;
90 struct arch_uprobe arch; 98 struct arch_uprobe arch;
91}; 99};
92 100
@@ -100,17 +108,12 @@ struct uprobe {
100 */ 108 */
101static bool valid_vma(struct vm_area_struct *vma, bool is_register) 109static bool valid_vma(struct vm_area_struct *vma, bool is_register)
102{ 110{
103 if (!vma->vm_file) 111 vm_flags_t flags = VM_HUGETLB | VM_MAYEXEC | VM_SHARED;
104 return false;
105
106 if (!is_register)
107 return true;
108 112
109 if ((vma->vm_flags & (VM_HUGETLB|VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) 113 if (is_register)
110 == (VM_READ|VM_EXEC)) 114 flags |= VM_WRITE;
111 return true;
112 115
113 return false; 116 return vma->vm_file && (vma->vm_flags & flags) == VM_MAYEXEC;
114} 117}
115 118
116static unsigned long offset_to_vaddr(struct vm_area_struct *vma, loff_t offset) 119static unsigned long offset_to_vaddr(struct vm_area_struct *vma, loff_t offset)
@@ -193,19 +196,44 @@ bool __weak is_swbp_insn(uprobe_opcode_t *insn)
193 return *insn == UPROBE_SWBP_INSN; 196 return *insn == UPROBE_SWBP_INSN;
194} 197}
195 198
199static void copy_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t *opcode)
200{
201 void *kaddr = kmap_atomic(page);
202 memcpy(opcode, kaddr + (vaddr & ~PAGE_MASK), UPROBE_SWBP_INSN_SIZE);
203 kunmap_atomic(kaddr);
204}
205
206static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t *new_opcode)
207{
208 uprobe_opcode_t old_opcode;
209 bool is_swbp;
210
211 copy_opcode(page, vaddr, &old_opcode);
212 is_swbp = is_swbp_insn(&old_opcode);
213
214 if (is_swbp_insn(new_opcode)) {
215 if (is_swbp) /* register: already installed? */
216 return 0;
217 } else {
218 if (!is_swbp) /* unregister: was it changed by us? */
219 return 0;
220 }
221
222 return 1;
223}
224
196/* 225/*
197 * NOTE: 226 * NOTE:
198 * Expect the breakpoint instruction to be the smallest size instruction for 227 * Expect the breakpoint instruction to be the smallest size instruction for
199 * the architecture. If an arch has variable length instruction and the 228 * the architecture. If an arch has variable length instruction and the
200 * breakpoint instruction is not of the smallest length instruction 229 * breakpoint instruction is not of the smallest length instruction
201 * supported by that architecture then we need to modify read_opcode / 230 * supported by that architecture then we need to modify is_swbp_at_addr and
202 * write_opcode accordingly. This would never be a problem for archs that 231 * write_opcode accordingly. This would never be a problem for archs that
203 * have fixed length instructions. 232 * have fixed length instructions.
204 */ 233 */
205 234
206/* 235/*
207 * write_opcode - write the opcode at a given virtual address. 236 * write_opcode - write the opcode at a given virtual address.
208 * @auprobe: arch breakpointing information.
209 * @mm: the probed process address space. 237 * @mm: the probed process address space.
210 * @vaddr: the virtual address to store the opcode. 238 * @vaddr: the virtual address to store the opcode.
211 * @opcode: opcode to be written at @vaddr. 239 * @opcode: opcode to be written at @vaddr.
@@ -216,8 +244,8 @@ bool __weak is_swbp_insn(uprobe_opcode_t *insn)
216 * For mm @mm, write the opcode at @vaddr. 244 * For mm @mm, write the opcode at @vaddr.
217 * Return 0 (success) or a negative errno. 245 * Return 0 (success) or a negative errno.
218 */ 246 */
219static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, 247static int write_opcode(struct mm_struct *mm, unsigned long vaddr,
220 unsigned long vaddr, uprobe_opcode_t opcode) 248 uprobe_opcode_t opcode)
221{ 249{
222 struct page *old_page, *new_page; 250 struct page *old_page, *new_page;
223 void *vaddr_old, *vaddr_new; 251 void *vaddr_old, *vaddr_new;
@@ -226,10 +254,14 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
226 254
227retry: 255retry:
228 /* Read the page with vaddr into memory */ 256 /* Read the page with vaddr into memory */
229 ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma); 257 ret = get_user_pages(NULL, mm, vaddr, 1, 0, 1, &old_page, &vma);
230 if (ret <= 0) 258 if (ret <= 0)
231 return ret; 259 return ret;
232 260
261 ret = verify_opcode(old_page, vaddr, &opcode);
262 if (ret <= 0)
263 goto put_old;
264
233 ret = -ENOMEM; 265 ret = -ENOMEM;
234 new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); 266 new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
235 if (!new_page) 267 if (!new_page)
@@ -264,63 +296,6 @@ put_old:
264} 296}
265 297
266/** 298/**
267 * read_opcode - read the opcode at a given virtual address.
268 * @mm: the probed process address space.
269 * @vaddr: the virtual address to read the opcode.
270 * @opcode: location to store the read opcode.
271 *
272 * Called with mm->mmap_sem held (for read and with a reference to
273 * mm.
274 *
275 * For mm @mm, read the opcode at @vaddr and store it in @opcode.
276 * Return 0 (success) or a negative errno.
277 */
278static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t *opcode)
279{
280 struct page *page;
281 void *vaddr_new;
282 int ret;
283
284 ret = get_user_pages(NULL, mm, vaddr, 1, 0, 1, &page, NULL);
285 if (ret <= 0)
286 return ret;
287
288 vaddr_new = kmap_atomic(page);
289 vaddr &= ~PAGE_MASK;
290 memcpy(opcode, vaddr_new + vaddr, UPROBE_SWBP_INSN_SIZE);
291 kunmap_atomic(vaddr_new);
292
293 put_page(page);
294
295 return 0;
296}
297
298static int is_swbp_at_addr(struct mm_struct *mm, unsigned long vaddr)
299{
300 uprobe_opcode_t opcode;
301 int result;
302
303 if (current->mm == mm) {
304 pagefault_disable();
305 result = __copy_from_user_inatomic(&opcode, (void __user*)vaddr,
306 sizeof(opcode));
307 pagefault_enable();
308
309 if (likely(result == 0))
310 goto out;
311 }
312
313 result = read_opcode(mm, vaddr, &opcode);
314 if (result)
315 return result;
316out:
317 if (is_swbp_insn(&opcode))
318 return 1;
319
320 return 0;
321}
322
323/**
324 * set_swbp - store breakpoint at a given address. 299 * set_swbp - store breakpoint at a given address.
325 * @auprobe: arch specific probepo