From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.6 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_SANE_1 autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 187E5C49EA6 for ; Thu, 24 Jun 2021 15:10:52 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id C900C6102A for ; Thu, 24 Jun 2021 15:10:51 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org C900C6102A Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=arm.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:In-Reply-To:MIME-Version:References: Message-ID:Subject:Cc:To:From:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=80+kTe+8mXVAQKK5a04FHkdJyBRSRUOnsgRTaX5xrmk=; b=jElgVbWKX5wGJp zBGfUipuH1qqst2XhM3W+Ft/4lTlGJnRNkWp/e7p5xmkZSrPsShCwSackWekzxk8uOdz3+o78YaW4 rLFRZuVjonSP4q/W503/JvpwQah7aCZe1m1npTTpile8eWIhN7qA6s6NMfykGo8bhdYb8kBqmotel 5OGBMAKA9Jh7o3KYJ6RiMXi0yDVJs9Fv7LpU22DOewc5I5+VrGvZJ2OeKYCi5sYXfY2eDRR4xymee MAHJ0MBTwnSRpBYB1v7qYoKy+mNcnDGCJk5XUosu8NGdQMgiNGgunqt27wEmSWPUq2JkIaW8JERGa vOR9SOmoamPZwkHVeaxQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1lwQyn-00FCZg-2W; Thu, 24 Jun 2021 15:09:21 +0000 Received: from mail.kernel.org ([198.145.29.99]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1lwQyi-00FCYY-VP for linux-arm-kernel@lists.infradead.org; Thu, 24 Jun 2021 15:09:18 +0000 Received: by mail.kernel.org (Postfix) with ESMTPSA id 66DDD613C3; Thu, 24 Jun 2021 15:09:14 +0000 (UTC) Date: Thu, 24 Jun 2021 16:09:11 +0100 From: Catalin Marinas To: Matthew Wilcox Cc: Christoph Hellwig , Chen Huang , Mark Rutland , Andrew Morton , Stephen Rothwell , Al Viro , Randy Dunlap , Will Deacon , Linux ARM , linux-mm , open list Subject: Re: [BUG] arm64: an infinite loop in generic_perform_write() Message-ID: <20210624150911.GA25097@arm.com> References: <20210623132223.GA96264@C02TD0UTHF1T.local> <1c635945-fb25-8871-7b34-f475f75b2caf@huawei.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.10.1 (2018-07-13) X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210624_080917_090791_32974F2E X-CRM114-Status: GOOD ( 25.84 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org On Thu, Jun 24, 2021 at 12:15:46PM +0100, Matthew Wilcox wrote: > On Thu, Jun 24, 2021 at 08:04:07AM +0100, Christoph Hellwig wrote: > > On Thu, Jun 24, 2021 at 04:24:46AM +0100, Matthew Wilcox wrote: > > > On Thu, Jun 24, 2021 at 11:10:41AM +0800, Chen Huang wrote: > > > > In userspace, I perform such operation: > > > > > > > > fd = open("/tmp/test", O_RDWR | O_SYNC); > > > > access_address = (char *)mmap(NULL, uio_size, PROT_READ, MAP_SHARED, uio_fd, 0); > > > > ret = write(fd, access_address + 2, sizeof(long)); > > > > > > ... you know that accessing this at unaligned offsets isn't going to > > > work. It's completely meaningless. Why are you trying to do it? > > > > We still should not cause an infinite loop in kernel space due to a > > a userspace programmer error. > > They're running as root and they've mapped some device memory. We can't > save them from themself. Imagine if they'd done this to the NVMe BAR. Ignoring the MMIO case for now, I can trigger the same infinite loop with MTE (memory tagging), something like: char *a; a = mmap(0, page_sz, PROT_READ | PROT_WRITE | PROT_MTE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); /* tag 0 is the default, set tag 1 for the next 16 bytes */ set_tag((unsigned long)(a + 16) | (1UL << 56)); /* uaccess to a[16] expected to fail */ bytes = write(fd, a + 14, 8); The iov_iter_fault_in_readable() check succeeds since a[14] has tag 0. However, the copy_from_user() attempts an unaligned 8-byte load which fails because of the mismatched tag from a[16]. The loop continues indefinitely. copy_from_user() is not required to squeeze in as much as possible. So I think the 1-byte read per page via iov_iter_fault_in_readable() is not sufficient to guarantee progress unless copy_from_user() also reads at least 1 byte. We could change raw_copy_from_user() to fall back to 1-byte read in case of a fault or fix this corner case in the generic code. A quick hack, re-attempting the access with one byte: ------------------8<------------------------- diff --git a/mm/filemap.c b/mm/filemap.c index 66f7e9fdfbc4..67059071460c 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3686,8 +3686,18 @@ ssize_t generic_perform_write(struct file *file, * because not all segments in the iov can be copied at * once without a pagefault. */ - bytes = min_t(unsigned long, PAGE_SIZE - offset, - iov_iter_single_seg_count(i)); + unsigned long single_seg_bytes = + min_t(unsigned long, PAGE_SIZE - offset, + iov_iter_single_seg_count(i)); + + /* + * Check for intra-page faults (arm64 MTE, SPARC ADI) + * and fall back to single byte. + */ + if (bytes > single_seg_bytes) + bytes = single_seg_bytes; + else + bytes = 1; goto again; } pos += copied; ------------------8<------------------------- Or a slightly different hack, trying to detect if the first segment was crossing a page boundary: ------------------8<------------------------- diff --git a/mm/filemap.c b/mm/filemap.c index 66f7e9fdfbc4..7d1c03f5f559 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3678,16 +3678,24 @@ ssize_t generic_perform_write(struct file *file, iov_iter_advance(i, copied); if (unlikely(copied == 0)) { + struct iovec v = iov_iter_iovec(i); + /* * If we were unable to copy any data at all, we must - * fall back to a single segment length write. + * fall back to a single segment length write or a + * single byte write (for intra-page faults - arm64 + * MTE or SPARC ADI). * * If we didn't fallback here, we could livelock - * because not all segments in the iov can be copied at - * once without a pagefault. + * because not all segments in the iov or data within + * a segment can be copied at once without a fault. */ - bytes = min_t(unsigned long, PAGE_SIZE - offset, - iov_iter_single_seg_count(i)); + if (((unsigned long)v.iov_base & PAGE_MASK) == + ((unsigned long)(v.iov_base + bytes) & PAGE_MASK)) + bytes = 1; + else + bytes = min_t(unsigned long, PAGE_SIZE - offset, + iov_iter_single_seg_count(i)); goto again; } pos += copied; ------------------8<------------------------- -- Catalin _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel