From: Vineet Gupta <Vineet.Gupta1@synopsys.com>
To: Arnd Bergmann <arnd@arndb.de>,
Khalid Aziz <khalid.aziz@oracle.com>,
Andrey Konovalov <andreyknvl@google.com>,
Andrew Morton <akpm@linux-foundation.org>,
Peter Zijlstra <peterz@infradead.org>,
Christian Brauner <christian.brauner@ubuntu.com>,
Kees Cook <keescook@chromium.org>, Ingo Molnar <mingo@kernel.org>,
Aleksa Sarai <cyphar@cyphar.com>,
Linus Torvalds <torvalds@linux-foundation.org>
Cc: linux-snps-arc@lists.infradead.org, linux-kernel@vger.kernel.org,
linux-arch@vger.kernel.org,
Vineet Gupta <Vineet.Gupta1@synopsys.com>
Subject: [RFC 2/4] lib/strncpy_from_user: Remove redundant user space pointer range check
Date: Tue, 14 Jan 2020 12:08:44 -0800 [thread overview]
Message-ID: <20200114200846.29434-3-vgupta@synopsys.com> (raw)
In-Reply-To: <20200114200846.29434-1-vgupta@synopsys.com>
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 <vgupta@synopsys.com>
---
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
WARNING: multiple messages have this Message-ID (diff)
From: Vineet Gupta <Vineet.Gupta1@synopsys.com>
To: Arnd Bergmann <arnd@arndb.de>,
Khalid Aziz <khalid.aziz@oracle.com>,
Andrey Konovalov <andreyknvl@google.com>,
Andrew Morton <akpm@linux-foundation.org>,
Peter Zijlstra <peterz@infradead.org>,
Christian Brauner <christian.brauner@ubuntu.com>,
Kees Cook <keescook@chromium.org>, Ingo Molnar <mingo@kernel.org>,
Aleksa Sarai <cyphar@cyphar.com>,
Linus Torvalds <torvalds@linux-foundation.org>
Cc: linux-arch@vger.kernel.org,
Vineet Gupta <Vineet.Gupta1@synopsys.com>,
linux-snps-arc@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: [RFC 2/4] lib/strncpy_from_user: Remove redundant user space pointer range check
Date: Tue, 14 Jan 2020 12:08:44 -0800 [thread overview]
Message-ID: <20200114200846.29434-3-vgupta@synopsys.com> (raw)
In-Reply-To: <20200114200846.29434-1-vgupta@synopsys.com>
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 <vgupta@synopsys.com>
---
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
_______________________________________________
linux-snps-arc mailing list
linux-snps-arc@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-snps-arc
next prev parent reply other threads:[~2020-01-14 20:08 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-01-14 20:08 [RFC 0/4] Switching ARC to optimized generic strncpy_from_user Vineet Gupta
2020-01-14 20:08 ` Vineet Gupta
2020-01-14 20:08 ` Vineet Gupta
2020-01-14 20:08 ` [RFC 1/4] asm-generic/uaccess: don't define inline functions if noinline lib/* in use Vineet Gupta
2020-01-14 20:08 ` Vineet Gupta
2020-01-14 20:57 ` Arnd Bergmann
2020-01-14 20:57 ` Arnd Bergmann
2020-01-15 23:01 ` Vineet Gupta
2020-01-15 23:01 ` Vineet Gupta
2020-01-16 11:43 ` Arnd Bergmann
2020-01-16 11:43 ` Arnd Bergmann
2020-01-14 21:32 ` Linus Torvalds
2020-01-14 21:32 ` Linus Torvalds
2020-01-15 9:08 ` Arnd Bergmann
2020-01-15 9:08 ` Arnd Bergmann
2020-01-15 14:12 ` Al Viro
2020-01-15 14:12 ` Al Viro
2020-01-15 14:21 ` Arnd Bergmann
2020-01-15 14:21 ` Arnd Bergmann
2020-01-14 20:08 ` Vineet Gupta [this message]
2020-01-14 20:08 ` [RFC 2/4] lib/strncpy_from_user: Remove redundant user space pointer range check Vineet Gupta
2020-01-14 21:22 ` Linus Torvalds
2020-01-14 21:22 ` Linus Torvalds
2020-01-14 21:52 ` Vineet Gupta
2020-01-14 21:52 ` Vineet Gupta
2020-01-14 23:46 ` Al Viro
2020-01-14 23:46 ` Al Viro
2020-01-15 14:42 ` Andrey Konovalov
2020-01-15 14:42 ` Andrey Konovalov
2020-01-15 14:42 ` Andrey Konovalov
2020-01-15 23:00 ` Vineet Gupta
2020-01-15 23:00 ` Vineet Gupta
2020-01-14 20:08 ` [RFC 3/4] ARC: uaccess: remove noinline variants of __strncpy_from_user() and friends Vineet Gupta
2020-01-14 20:08 ` Vineet Gupta
2020-01-14 20:08 ` [RFC 4/4] ARC: uaccess: use optimized generic __strnlen_user/__strncpy_from_user Vineet Gupta
2020-01-14 20:08 ` Vineet Gupta
2020-01-14 20:42 ` Arnd Bergmann
2020-01-14 20:42 ` Arnd Bergmann
2020-01-14 21:36 ` Vineet Gupta
2020-01-14 21:36 ` Vineet Gupta
2020-01-14 21:49 ` Linus Torvalds
2020-01-14 21:49 ` Linus Torvalds
2020-01-14 22:14 ` Vineet Gupta
2020-01-14 22:14 ` Vineet Gupta
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200114200846.29434-3-vgupta@synopsys.com \
--to=vineet.gupta1@synopsys.com \
--cc=akpm@linux-foundation.org \
--cc=andreyknvl@google.com \
--cc=arnd@arndb.de \
--cc=christian.brauner@ubuntu.com \
--cc=cyphar@cyphar.com \
--cc=keescook@chromium.org \
--cc=khalid.aziz@oracle.com \
--cc=linux-arch@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-snps-arc@lists.infradead.org \
--cc=mingo@kernel.org \
--cc=peterz@infradead.org \
--cc=torvalds@linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.