Received: by 2002:a25:8b91:0:0:0:0:0 with SMTP id j17csp5628234ybl; Tue, 14 Jan 2020 12:10:06 -0800 (PST) X-Google-Smtp-Source: APXvYqwO7BvquTqRxkN0u9tEZF5SMPerOSaSN9q1koSzPJWfUbZfF/Rbom/69gP7RfqeVCbx8s/r X-Received: by 2002:aca:36c1:: with SMTP id d184mr18333652oia.70.1579032606632; Tue, 14 Jan 2020 12:10:06 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1579032606; cv=none; d=google.com; s=arc-20160816; b=p4ZHXzDL3KolzXu1UASaFhuFaDDnLPd1ZvTSnzJjuTBtZ8Qj5KmsFhRe2GtEuAaoV8 S+yjnklpBwg9rinwE+bIjjEK7SNm83JvUlkM2agyK9j7zZVZ5tuCI7x9XBns+PM4lmAn J4wtQtnYlWv+VBrcOUyEu7GEnSUdoJDwkzDylWnfywGdRam7bBq57L5Zc6L+eWx20nGj FKE9R4yV0ms9/lIcIMlndHtcAm7B1cFOEqya6UGgDtCL+j3Y+ZCeJAL9cnf/vWV7youq BeWkeiCIGtti6k+QUHyAe7QNOR/XiwyausT9l6LwqjjuWuES1DmFrGd7+Im312ZMm+6B AH5w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=nimmJku/pgg+S4+R8KX9e5bep4gLI9465nGgpoo7Ddg=; b=i/jNyPDLQUl3ASnE86KmsroE2lzpVg/tqeKXzjoubjeqUghMSGNzMuGMwbufrgclGJ PWZTHp7Pjz0caNlkwtMosqSF86b+7DCE9q9T0gIRVIos6mbDBng/QntEneRLM1cC2fZT 0zXSUz1CTBJt9LJdfePPpra74qCggJYSxL9y6igHlz7rAhD9vJ0MyG+sa3LMu8SLJOej a0i/tBLmsYLnSFlgHlFFNeyZlQhz6EHzcx2pOoFnjrf9BeuAWs6q2eFDoxaQPPy36xVw r4d1qI9oUEPPHFCEe3QC7pK+qNxD8lGl26EFEgXlnv7YHcb8sQ+DJKnNfWbN+/Ubw+eH Zq2w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@synopsys.com header.s=mail header.b=DpI5XmdX; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=synopsys.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id a9si8173953oib.59.2020.01.14.12.09.53; Tue, 14 Jan 2020 12:10:06 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@synopsys.com header.s=mail header.b=DpI5XmdX; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=synopsys.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728847AbgANUI7 (ORCPT + 99 others); Tue, 14 Jan 2020 15:08:59 -0500 Received: from sv2-smtprelay2.synopsys.com ([149.117.73.133]:53248 "EHLO smtprelay-out1.synopsys.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728748AbgANUI5 (ORCPT ); Tue, 14 Jan 2020 15:08:57 -0500 Received: from mailhost.synopsys.com (sv2-mailhost1.synopsys.com [10.205.2.133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by smtprelay-out1.synopsys.com (Postfix) with ESMTPS id CB322406F1; Tue, 14 Jan 2020 20:08:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=synopsys.com; s=mail; t=1579032536; bh=KAlf2p4wGz+94y3EQ9KwXhmJn1lHpeOnvb6o+KcrGBk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DpI5XmdXeJkr91JuxRySFxxmKXApulm2WYX9JWR9+OCQPtQWErGMGaVN6WiOt76AG XZ00QkKk42oXLoMmeJ1DZUbz82tvlve7E1H0k3RDaLpA6NWvJopjStxXRUgNrvEgcl hSwhuBcsmna5s0GC7Vl221yPHmO7cVX23P0fip83vYlkEjRqRbHsCp8JIZjHNesaFv Rl57rinxodXZmq499z23UVCsOxNXRgVvkYGXdoglNYzD+mao6ArwEneVc0wbS2DZRe Ioc/2tiv0XL+ZHlWB4mHeZ6DOcYnqS5dfDmCKtUluM7Vz+J98ACKS+24/jwieZAIHD KQvNiMm8vqiqA== Received: from vineetg-Latitude-E7450.internal.synopsys.com (vineetg-latitude-e7450.internal.synopsys.com [10.10.161.25]) by mailhost.synopsys.com (Postfix) with ESMTP id 5A9E2A00A0; Tue, 14 Jan 2020 20:08:55 +0000 (UTC) From: Vineet Gupta To: Arnd Bergmann , Khalid Aziz , Andrey Konovalov , Andrew Morton , Peter Zijlstra , Christian Brauner , Kees Cook , Ingo Molnar , Aleksa Sarai , Linus Torvalds Cc: linux-snps-arc@lists.infradead.org, linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org, Vineet Gupta Subject: [RFC 2/4] lib/strncpy_from_user: Remove redundant user space pointer range check Date: Tue, 14 Jan 2020 12:08:44 -0800 Message-Id: <20200114200846.29434-3-vgupta@synopsys.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200114200846.29434-1-vgupta@synopsys.com> References: <20200114200846.29434-1-vgupta@synopsys.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This came up when switching ARC to word-at-a-time interface and using generic/optimized strncpy_from_user It seems the existing code checks for user buffer/string range multiple times and one of tem cn be avoided. There's an open-coded range check which computes @max off of user_addr_max() and thus typically way larger than the kernel buffer @count and subsequently discarded in do_strncpy_from_user() if (max > count) max = count; The canonical user_access_begin() => access_ok() follow anyways and even with @count it should suffice for an intial range check as is true for any copy_{to,from}_user() And in case actual user space buffer is smaller than kernel dest pointer (i.e. @max < @count) the usual string copy, null byte detection would abort the process early anyways Signed-off-by: Vineet Gupta --- lib/strncpy_from_user.c | 36 +++++++++++------------------------- lib/strnlen_user.c | 28 +++++++--------------------- 2 files changed, 18 insertions(+), 46 deletions(-) diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c index dccb95af6003..a1622d71f037 100644 --- a/lib/strncpy_from_user.c +++ b/lib/strncpy_from_user.c @@ -21,22 +21,15 @@ /* * Do a strncpy, return length of string without final '\0'. * 'count' is the user-supplied count (return 'count' if we - * hit it), 'max' is the address space maximum (and we return - * -EFAULT if we hit it). + * hit it). If access fails, return -EFAULT. */ static inline long do_strncpy_from_user(char *dst, const char __user *src, - unsigned long count, unsigned long max) + unsigned long count) { const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; + unsigned long max = count; unsigned long res = 0; - /* - * Truncate 'max' to the user-specified limit, so that - * we only have one limit we need to check in the loop - */ - if (max > count) - max = count; - if (IS_UNALIGNED(src, dst)) goto byte_at_a_time; @@ -72,7 +65,7 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, * Uhhuh. We hit 'max'. But was that the user-specified maximum * too? If so, that's ok - we got as much as the user asked for. */ - if (res >= count) + if (res == count) return res; /* @@ -103,25 +96,18 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, */ long strncpy_from_user(char *dst, const char __user *src, long count) { - unsigned long max_addr, src_addr; - if (unlikely(count <= 0)) return 0; - max_addr = user_addr_max(); - src_addr = (unsigned long)untagged_addr(src); - if (likely(src_addr < max_addr)) { - unsigned long max = max_addr - src_addr; + kasan_check_write(dst, count); + check_object_size(dst, count, false); + if (user_access_begin(src, count)) { long retval; - - kasan_check_write(dst, count); - check_object_size(dst, count, false); - if (user_access_begin(src, max)) { - retval = do_strncpy_from_user(dst, src, count, max); - user_access_end(); - return retval; - } + retval = do_strncpy_from_user(dst, src, count); + user_access_end(); + return retval; } + return -EFAULT; } EXPORT_SYMBOL(strncpy_from_user); diff --git a/lib/strnlen_user.c b/lib/strnlen_user.c index 6c0005d5dd5c..5ce61f303d6e 100644 --- a/lib/strnlen_user.c +++ b/lib/strnlen_user.c @@ -20,19 +20,13 @@ * if it fits in a aligned 'long'. The caller needs to check * the return value against "> max". */ -static inline long do_strnlen_user(const char __user *src, unsigned long count, unsigned long max) +static inline long do_strnlen_user(const char __user *src, unsigned long count) { const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; unsigned long align, res = 0; + unsigned long max = count; unsigned long c; - /* - * Truncate 'max' to the user-specified limit, so that - * we only have one limit we need to check in the loop - */ - if (max > count) - max = count; - /* * Do everything aligned. But that means that we * need to also expand the maximum.. @@ -64,7 +58,7 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count, * Uhhuh. We hit 'max'. But was that the user-specified maximum * too? If so, return the marker for "too long". */ - if (res >= count) + if (res == count) return count+1; /* @@ -98,22 +92,14 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count, */ long strnlen_user(const char __user *str, long count) { - unsigned long max_addr, src_addr; - if (unlikely(count <= 0)) return 0; - max_addr = user_addr_max(); - src_addr = (unsigned long)untagged_addr(str); - if (likely(src_addr < max_addr)) { - unsigned long max = max_addr - src_addr; + if (user_access_begin(str, count)) { long retval; - - if (user_access_begin(str, max)) { - retval = do_strnlen_user(str, count, max); - user_access_end(); - return retval; - } + retval = do_strnlen_user(str, count); + user_access_end(); + return retval; } return 0; } -- 2.20.1