From patchwork Mon Feb 10 22:16:24 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jordan Rome X-Patchwork-Id: 13968633 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7F7D1C0219E for ; Mon, 10 Feb 2025 22:17:14 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 0FB8828000D; Mon, 10 Feb 2025 17:17:14 -0500 (EST) Received: by kanga.kvack.org (Postfix, from userid 40) id 0AC68280006; Mon, 10 Feb 2025 17:17:14 -0500 (EST) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id E664028000D; Mon, 10 Feb 2025 17:17:13 -0500 (EST) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0015.hostedemail.com [216.40.44.15]) by kanga.kvack.org (Postfix) with ESMTP id C33E1280006 for ; Mon, 10 Feb 2025 17:17:13 -0500 (EST) Received: from smtpin09.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay06.hostedemail.com (Postfix) with ESMTP id 46A0FB0983 for ; Mon, 10 Feb 2025 22:17:13 +0000 (UTC) X-FDA: 83105446746.09.FC7E5C8 Received: from mout.perfora.net (mout.perfora.net [74.208.4.194]) by imf11.hostedemail.com (Postfix) with ESMTP id 8928240014 for ; Mon, 10 Feb 2025 22:17:11 +0000 (UTC) Authentication-Results: imf11.hostedemail.com; dkim=pass header.d=jordanrome.com header.s=s1-ionos header.b=vCiWUozh; spf=pass (imf11.hostedemail.com: domain of linux@jordanrome.com designates 74.208.4.194 as permitted sender) smtp.mailfrom=linux@jordanrome.com; dmarc=none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1739225831; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-transfer-encoding:content-transfer-encoding: in-reply-to:references:dkim-signature; bh=yJJHtP6Ybd5+FScfldyOtf8Z7tbm+0cUAU9G4HHYpKQ=; b=s5Vw8Wu9Xtv45rPpxP5lcfDgkU/cxillUlo1ml2z4I7pgwXssqdOnr0oHjmA/wDsWw6rVx NAUX+RHysTxU8+BMS5mc9SrapRj60X41NLf+WDFJq2NeugtVBG6A6jG8NHRUVtyaKqsJYO /l9myflJIJ6OJCevU0i6TB2XjFx/7EQ= ARC-Authentication-Results: i=1; imf11.hostedemail.com; dkim=pass header.d=jordanrome.com header.s=s1-ionos header.b=vCiWUozh; spf=pass (imf11.hostedemail.com: domain of linux@jordanrome.com designates 74.208.4.194 as permitted sender) smtp.mailfrom=linux@jordanrome.com; dmarc=none ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1739225831; a=rsa-sha256; cv=none; b=PCoWNRpFNktSEcfDJtVoaMwjWO4P1xfsWkbWmA5TRSyK9vM+aYpHeJitUNWuU8gWdm4LAa 2Ux3vn1lnxOa+Zw8+Neyo+BuUWvSZMbysItsW6k2PDh479vBjmUIQ1jJHt71qDCeHozJw8 5IuVM56RucGabGty2byPCLgIF0EqasE= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jordanrome.com; s=s1-ionos; t=1739225791; x=1739830591; i=linux@jordanrome.com; bh=yJJHtP6Ybd5+FScfldyOtf8Z7tbm+0cUAU9G4HHYpKQ=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:Message-ID: MIME-Version:Content-Transfer-Encoding:cc: content-transfer-encoding:content-type:date:from:message-id: mime-version:reply-to:subject:to; b=vCiWUozhWlHq24G/JPxCPvVRfW8HDUhMwQeSCHtWxDbwQJGo7PC6Va/Nt8145ZVJ LHr4q+JOvbEv1GBcdApwnQLqmTtBNfg1Ck+l1Zliq7WBbViwNiG6MXSS1k0usNqsy re+eyCVsyJVkswO1CmOHf6yoHT+oTk7JJUsDE0rGOE5sG8+F506S1zWR2fLoD+u9F ODs4HiQvgoeGfcQjLXQYe8mYFl4Tn2ofIKmqXOO1fgML2hi0fUJPauDtTxB16nLPC ViK9SBrUhfShxW9l7e/7K0DzKrpRe9J/Wz0rOWrO/tZM2lMvaHtkWU08MV37SK7oZ alj+RYt8PHw/eaLF4g== X-UI-Sender-Class: 55c96926-9e95-11ee-ae09-1f7a4046a0f6 Received: from localhost ([69.171.251.115]) by mrelay.perfora.net (mreueus004 [74.208.5.2]) with ESMTPSA (Nemesis) id 1MsZaJ-1tNb9Q017L-00vbnZ; Mon, 10 Feb 2025 23:16:31 +0100 From: Jordan Rome To: bpf@vger.kernel.org Cc: linux-mm@kvack.org, Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Kernel Team , Andrew Morton , Shakeel Butt , Alexander Potapenko Subject: [bpf-next v7 1/3] mm: add copy_remote_vm_str Date: Mon, 10 Feb 2025 14:16:24 -0800 Message-ID: <20250210221626.2098522-1-linux@jordanrome.com> X-Mailer: git-send-email 2.43.5 MIME-Version: 1.0 X-Provags-ID: V03:K1:QNjPbdBmC6RgFhoMhi+mCZ4fNXpSAQszghe/PMKyIZTi/DfAfXf w7He5TL2nFLQABaNZqxupI7lA055F6UPvNK4p0YMNMT/M+dH8hbX+86FBWrdqurL6a3EBNx UJD3iOy09SzWgKSVQc18fTop5DM/yC1/DU2v7xFECrjFgJBKjY9dWeWnrpBz6Hgx24UHGGz m6Yf1klZNJmcSdXy0tQlg== UI-OutboundReport: notjunk:1;M01:P0:71o24VuHdlQ=;wPcmLcdhKW4VuPuIyLJ3k0Sjd/c 6Tz0sWNDUlBlmi4rEJcLEy/64yNEqKUucdKzAadGGsO4MyMV5uuVwJAeEgJQ7mmSkK4FzZVJN j9q4gZLlx9jwMNjHDjoN5Pgn33CJX68lgK1qN0VxZIsGMqMvprkQAVVmA38672AI+oIL+jKe9 2UZ+mC7Ohp6RSSP2W9xvYUxKqFQcrty1+K7YcRIr3IezgjmJ0Ns3c+PGW1/ByHakXr0zU44QE dm9vbMp5xXfwwqtZdDIb1m0VY8e8mZ0gv9MuS3TCsRLt38xXJrnp844Me+8fVGgTC5jVZi5X4 CSJcr+4m+ObHGRgqZr4g6bv3GbVvJM8GV2r9U2fRLm6Y/TimCN8X8grGzUww7hh3do9Lx7bWQ gv55dxTZKfcIA0K6xy5EP6LXG8BEMBKIF2l9DLatbcW9aQ/Uyolx5y434EGmK8wI3PAD5naqe akdxzx+BUihOD0KazfNwWqBkAEMr7mjU4l8KSRjHufNGcB+FFm2JeKPuIJ1e35jMzqBEJMuNN 0wccjl/V3yTUgBMd4LnyKUJKVJgi2APkeB6DqPu6nK2dA4ynkPl2u8zo85yEuWgC8DitQkOGM ceu+RujZE+YosngqKc+PWeqjSuClF34m2DQhY+qmAcfqWFbtwNY/+HtmC77t9hZCYppvgxr0z 0sE2FwQB/CWNLc4zo9wdcBpi2OuDZprE05KUyqV6tqN1nXWBbvPMvDUyH43ifggKlGQ1In+NK KC8DfeHoxCrOuol4o03qeK11IK7vgRCzGVZ25S67r9WUaCDIzasG/VFJ/OTbqZqlm1pSFmrJO Iczu/utXrcPC655dGqWuBlNduTJBDNao4m0pAMYdagusYFX1HhxxFyF6uJtQDZfeNB1EcHP+a Y2SW1B/t25UiVFqDuZCLrCrnbb5AhCf+PH40vPPZFE+zpe4Zn+TS/GqjA0vgKIY8ZM+Y6K+KI t9OhEjrnSqr2MfcvZe5PhJiuvKwCYv3kLvBTW2Cgz/fxXhKk64gN5+BP/7jqrKRr0Jdt6/i/p TeIoJnWsKhvsE6z1vijj70gxvG/vGYhVOzP247cvmRO/NQZrqCQHvX4iUhvrmpLyV1ZQkvfTB SIeLXIFdKX2cKSyrMNRnlagKyWGBzxIrHtO+ti09Nws5bMfdtZViScg3t/hFiFCKpMQJoViix zWe3Brc7Sk3fFfbSDe4B6i/Wk0cK57t3P2v4Pjcd8WhtNbvLZ+eOWspNwBm9kvwDCeY7fm3vh N3dkBOeGC/5ZHIdPNfu1ea4wf5F1GTcikiS/3Xkd5sgRbL0VIyNdd1EuDZSUbhyFFT69BGdkg KZE9lhG9z3k6g2UChXarYe8Utb6UW1huCDs+iucAXWJPPU= X-Rspam-User: X-Stat-Signature: f6d4pruwzsb6tpy59m9na95i1r1jxkw5 X-Rspamd-Server: rspam05 X-Rspamd-Queue-Id: 8928240014 X-HE-Tag: 1739225831-140513 X-HE-Meta: U2FsdGVkX1+tldOttzRxxk9Q20RCya9UBzNAnhYR/XOLRcfblBOec2ExKyXyAg0hhmZLdO0aWsM/MgNlZeR7AfiHXioXZbWyo0DA+RdqMi96JOBhCfZ8KgSNnWm8sgBCXL90lgB2sNfhC4uJv9dN7YiKLeNlCdsR4YU0ZGCPkihAn6U6LDv7lKgSkYNMXLmT9m5AePtraxRQLtAx1yBILEIuSgvD1RrWxRJODT9TlrtoiXNhMTO/xsU+/DkIT58YqomiggAm/TiIZgmJC1rKSaLlg2surw58BkFd6dystIsWOvpBDdWRmE+VZ7EB/vgCEFz5ASeFVLR8OydBdghGvswOHX2hx3Jrv2Jpre/nXQK9HamKQME6dkrYUvKPVp8MJhflEUQ3fH8gsPKSkoGtSLOG73Ur6efDOVfTwBJxyQUF6VIIw8iPA6YwyBezmZnMjxLNCktQ5ouSC/Vpt+90BmlOjc9FcfRn0/g/YvYBDo4c53qhLjHosBxwWitL/R+Yt/+HzsyLd/07NEJxg4HoFJ1JEEvSUJb3fq+3YseUSdZJw87gcTpSGTiuvV5turbw3TUR+PqAW27n1ejPkL/SuLl70cCS6EkmXECwjVlLK9ZN1PR2k5p28EfXXxhcyL9oEUwIUD8bqAtlkYegt/RoeCY7+3Hrfhxt4FMBgORBm6sTAjYzh6NnemyRNfkS3cIJJlWvu0jP7WJMxGOlFkx18hZzs6L0bKmAXQ19JAPqFrkTczQW6KjZyg0tZVnHT00puVU78FwaHVbQjecl+33XaGNro1xW018Z+qEdERbmY0TpwyLHiL84ubEiVSfRLvHoF2w758hD3OXU3pi7vNTaQYaEukPxJceb5B1rsAeluEB53QB87zEW3yt6/boiV5ApHZ2kaHoGh0dpv226y3rTYyX6Sxafjui1ed9okQfBJGuwokGJLHw2qMN2zzVShqSGMdwzarfOmjpcYIehaXa CxxOArzo SVCCm/apojg1IS6Z0/9UZ/f2AkY5ceCyg/51YIVgwk7JIOt3fkaw8H4t2RxVRNLDdTVCAggd/XPPWgFNcgKTDbfz1Siw2GrvNdccRZKII4NtEk6GDJpGedAql34l7M3pn9KFqhB1otaaxpSW/dsU8gtcfvJDlSgYLszMI2QSuXnG+VYqLKSdrlN8rX75bd09adzL8fgC8GZLu61hMTtQCEp7owZjKEkIzm2/R/HPnxXqdndo+eG+Mzq8SEyt1YeBmH9ZG9qCzwILnu4KKLtrb28UxOy1p41txjQRLDC4X9E83MnxL7wLKy71o480etYytno/Q+n/LQ23QgSXTSHiliTSy3ffWkMs3vh52FJATOkguc72E/D78WA5fK9/QojT9ay8l X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: List-Subscribe: List-Unsubscribe: Similar to `access_process_vm` but specific to strings. Also chunks reads by page and utilizes `strscpy` for handling null termination. Signed-off-by: Jordan Rome Acked-by: Andrii Nakryiko Reviewed-by: Shakeel Butt --- include/linux/mm.h | 3 ++ mm/memory.c | 119 +++++++++++++++++++++++++++++++++++++++++++++ mm/nommu.c | 73 +++++++++++++++++++++++++++ 3 files changed, 195 insertions(+) -- 2.43.5 diff --git a/include/linux/mm.h b/include/linux/mm.h index 7b1068ddcbb7..aee23d84ce01 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2486,6 +2486,9 @@ extern int access_process_vm(struct task_struct *tsk, unsigned long addr, extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, void *buf, int len, unsigned int gup_flags); +extern int copy_remote_vm_str(struct task_struct *tsk, unsigned long addr, + void *buf, int len, unsigned int gup_flags); + long get_user_pages_remote(struct mm_struct *mm, unsigned long start, unsigned long nr_pages, unsigned int gup_flags, struct page **pages, diff --git a/mm/memory.c b/mm/memory.c index 539c0f7c6d54..e9d8584a7f56 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -6803,6 +6803,125 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, } EXPORT_SYMBOL_GPL(access_process_vm); +/* + * Copy a string from another process's address space as given in mm. + * If there is any error return -EFAULT. + */ +static int __copy_remote_vm_str(struct mm_struct *mm, unsigned long addr, + void *buf, int len, unsigned int gup_flags) +{ + void *old_buf = buf; + int err = 0; + + *(char *)buf = '\0'; + + if (mmap_read_lock_killable(mm)) + return -EFAULT; + + /* Untag the address before looking up the VMA */ + addr = untagged_addr_remote(mm, addr); + + /* Avoid triggering the temporary warning in __get_user_pages */ + if (!vma_lookup(mm, addr)) { + err = -EFAULT; + goto out; + } + + while (len) { + int bytes, offset, retval; + void *maddr; + struct page *page; + struct vm_area_struct *vma = NULL; + + page = get_user_page_vma_remote(mm, addr, gup_flags, &vma); + + if (IS_ERR(page)) { + /* + * Treat as a total failure for now until we decide how + * to handle the CONFIG_HAVE_IOREMAP_PROT case and + * stack expansion. + */ + *(char *)buf = '\0'; + err = -EFAULT; + goto out; + } + + bytes = len; + offset = addr & (PAGE_SIZE - 1); + if (bytes > PAGE_SIZE - offset) + bytes = PAGE_SIZE - offset; + + maddr = kmap_local_page(page); + retval = strscpy(buf, maddr + offset, bytes); + + if (retval >= 0) { + /* Found the end of the string */ + buf += retval; + unmap_and_put_page(page, maddr); + break; + } + + buf += bytes - 1; + /* + * Because strscpy always NUL terminates we need to + * copy the last byte in the page if we are going to + * load more pages + */ + if (bytes != len) { + addr += bytes - 1; + copy_from_user_page(vma, page, addr, buf, + maddr + (PAGE_SIZE - 1), 1); + + buf += 1; + addr += 1; + } + len -= bytes; + + unmap_and_put_page(page, maddr); + } + +out: + mmap_read_unlock(mm); + if (err) + return err; + + return buf - old_buf; +} + +/** + * copy_remote_vm_str - copy a string from another process's address space. + * @tsk: the task of the target address space + * @addr: start address to read from + * @buf: destination buffer + * @len: number of bytes to copy + * @gup_flags: flags modifying lookup behaviour + * + * The caller must hold a reference on @mm. + * + * Return: number of bytes copied from @addr (source) to @buf (destination); + * not including the trailing NUL. Always guaranteed to leave NUL-terminated + * buffer. On any error, return -EFAULT. + */ +int copy_remote_vm_str(struct task_struct *tsk, unsigned long addr, + void *buf, int len, unsigned int gup_flags) +{ + struct mm_struct *mm; + int ret; + + mm = get_task_mm(tsk); + if (!mm) { + *(char *)buf = '\0'; + return -EFAULT; + } + + ret = __copy_remote_vm_str(mm, addr, buf, len, gup_flags); + + mmput(mm); + + return ret; +} +EXPORT_SYMBOL_GPL(copy_remote_vm_str); + /* * Print the name of a VMA. */ diff --git a/mm/nommu.c b/mm/nommu.c index baa79abdaf03..ec3bfcb35215 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1708,6 +1708,79 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in } EXPORT_SYMBOL_GPL(access_process_vm); +/* + * Copy a string from another process's address space as given in mm. + * If there is any error return -EFAULT. + */ +static int __copy_remote_vm_str(struct mm_struct *mm, unsigned long addr, + void *buf, int len) +{ + unsigned long addr_end; + struct vm_area_struct *vma; + int ret = -EFAULT; + + *(char *)buf = '\0'; + + if (mmap_read_lock_killable(mm)) + return ret; + + /* the access must start within one of the target process's mappings */ + vma = find_vma(mm, addr); + if (!vma) + goto out; + + if (check_add_overflow(addr, len, &addr_end)) + goto out; + /* don't overrun this mapping */ + if (addr_end > vma->vm_end) + len = vma->vm_end - addr; + + /* only read mappings where it is permitted */ + if (vma->vm_flags & VM_MAYREAD) { + ret = strscpy(buf, (char *)addr, len); + if (ret < 0) + ret = len - 1; + } + +out: + mmap_read_unlock(mm); + return ret; +} + +/** + * copy_remote_vm_str - copy a string from another process's address space. + * @tsk: the task of the target address space + * @addr: start address to read from + * @buf: destination buffer + * @len: number of bytes to copy + * @gup_flags: flags modifying lookup behaviour (unused) + * + * The caller must hold a reference on @mm. + * + * Return: number of bytes copied from @addr (source) to @buf (destination); + * not including the trailing NUL. Always guaranteed to leave NUL-terminated + * buffer. On any error, return -EFAULT. + */ +int copy_remote_vm_str(struct task_struct *tsk, unsigned long addr, + void *buf, int len, unsigned int gup_flags) +{ + struct mm_struct *mm; + int ret; + + mm = get_task_mm(tsk); + if (!mm) { + *(char *)buf = '\0'; + return -EFAULT; + } + + ret = __copy_remote_vm_str(mm, addr, buf, len); + + mmput(mm); + + return ret; +} +EXPORT_SYMBOL_GPL(copy_remote_vm_str); + /** * nommu_shrink_inode_mappings - Shrink the shared mappings on an inode * @inode: The inode to check