Refactor copy_insn into a more generic uprobe_copy_from_file. The existing copy_insn is still there, but uses the generic version now. The generic version will be used in later patches.
No semantic change intended. Assisted-by: omp:gpt-5.6-luna Signed-off-by: Andi Kleen <[email protected]> --- include/linux/uprobes.h | 2 ++ kernel/events/uprobes.c | 59 +++++++++++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 6cea2944ca8b..6fa70f3f648c 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -304,6 +304,8 @@ extern void uprobe_handle_trampoline(struct pt_regs *regs); extern void *arch_uretprobe_trampoline(unsigned long *psize); extern unsigned long uprobe_get_trampoline_vaddr(void); extern void uprobe_copy_from_page(struct page *page, unsigned long vaddr, void *dst, int len); +extern int uprobe_copy_from_file(struct inode *inode, struct file *file, + loff_t offset, void *buf, int size); extern void arch_uprobe_clear_state(struct mm_struct *mm); extern void arch_uprobe_init_state(struct mm_struct *mm); extern void handle_syscall_uprobe(struct pt_regs *regs, unsigned long bp_vaddr); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 30c28625bb5f..894196f3089f 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1073,30 +1073,55 @@ static int __copy_insn(struct address_space *mapping, struct file *filp, return 0; } -static int copy_insn(struct uprobe *uprobe, struct file *filp) +/** + * uprobe_copy_from_file - read bytes from a file's page cache + * @inode: the file's inode + * @file: file used by the filesystem's read_folio callback + * @offset: byte offset into the file + * @buf: destination buffer + * @size: number of bytes to read (may cross page boundaries) + * + * Callers that require a full instruction must check that the requested size + * was copied. + */ +int uprobe_copy_from_file(struct inode *inode, struct file *file, + loff_t offset, void *buf, int size) { - struct address_space *mapping = uprobe->inode->i_mapping; - loff_t offs = uprobe->offset; - void *insn = &uprobe->arch.insn; - int size = sizeof(uprobe->arch.insn); - int len, err = -EIO; + struct address_space *mapping = inode->i_mapping; + int len, copied = 0, err; - /* Copy only available bytes, -EIO if nothing was read */ - do { - if (offs >= i_size_read(uprobe->inode)) + if (offset < 0 || size < 0) + return -EINVAL; + + while (copied < size) { + if (offset >= i_size_read(inode)) break; - len = min_t(int, size, PAGE_SIZE - (offs & ~PAGE_MASK)); - err = __copy_insn(mapping, filp, insn, len, offs); + /* Let the page cache zero-fill bytes past i_size in the final page. */ + len = min_t(int, size - copied, + PAGE_SIZE - (offset & ~PAGE_MASK)); + err = __copy_insn(mapping, file, buf + copied, len, offset); if (err) - break; + return err; + + copied += len; + offset += len; + } + return copied; +} - insn += len; - offs += len; - size -= len; - } while (size); +static int copy_insn(struct uprobe *uprobe, struct file *filp) +{ + int ret; - return err; + ret = uprobe_copy_from_file(uprobe->inode, filp, uprobe->offset, + &uprobe->arch.insn, + sizeof(uprobe->arch.insn)); + if (ret < 0) + return ret; + if (!ret) + return -EIO; + return 0; } static int prepare_uprobe(struct uprobe *uprobe, struct file *file, -- 2.54.0
