linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] Fix FORTIFY=y UBSAN_LOCAL_BOUNDS=y
@ 2022-08-30 20:53 Nick Desaulniers
  2022-08-30 20:53 ` [PATCH 1/3] fortify: use __builtin_dynamic_object_size in __compiletime_strlen Nick Desaulniers
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Nick Desaulniers @ 2022-08-30 20:53 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Tom Rix, linux-hardening, linux-kernel, llvm,
	Jiri Kosina, Benjamin Tissoires, linux-input, Masahiro Yamada,
	Nick Desaulniers

With CONFIG_FORTIFY=y and CONFIG_UBSAN_LOCAL_BOUNDS=y enabled, we
observe a runtime panic while running Android's Compatibility Test
Suite's (CTS) android.hardware.input.cts.tests.  This is stemming from a
strlen() call in hidinput_allocate().

__builtin_object_size(str, 0 or 1) has interesting behavior for C
strings when str is runtime dependent, and all possible values are known
at compile time; it evaluates to the maximum of those sizes. This causes
UBSAN_LOCAL_BOUNDS to insert faults for the smaller values, which we
trip at runtime.

Patch 1 adds a KCONFIG version check for __builtin_dynamic_object_size,
and uses that in __compiletime_strlen rather than __builtin_object_size.
Patch 2 and 3 are cosmetic cleanups, they're not as important to me as
patch 1 is.

Nick Desaulniers (3):
  fortify: use __builtin_dynamic_object_size in __compiletime_strlen
  fortify: cosmetic cleanups to __compiletime_strlen
  HID: avoid runtime call to strlen

 drivers/hid/hid-input.c        | 13 ++++++++++++-
 include/linux/fortify-string.h | 15 ++++++++++-----
 init/Kconfig                   |  3 +++
 3 files changed, 25 insertions(+), 6 deletions(-)

-- 
2.37.2.672.g94769d06f0-goog


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/3] fortify: use __builtin_dynamic_object_size in __compiletime_strlen
  2022-08-30 20:53 [PATCH 0/3] Fix FORTIFY=y UBSAN_LOCAL_BOUNDS=y Nick Desaulniers
@ 2022-08-30 20:53 ` Nick Desaulniers
  2022-08-31 18:34   ` Kees Cook
  2022-08-30 20:53 ` [PATCH 2/3] fortify: cosmetic cleanups to __compiletime_strlen Nick Desaulniers
  2022-08-30 20:53 ` [PATCH 3/3] HID: avoid runtime call to strlen Nick Desaulniers
  2 siblings, 1 reply; 8+ messages in thread
From: Nick Desaulniers @ 2022-08-30 20:53 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Tom Rix, linux-hardening, linux-kernel, llvm,
	Jiri Kosina, Benjamin Tissoires, linux-input, Masahiro Yamada,
	Nick Desaulniers

With CONFIG_FORTIFY=y and CONFIG_UBSAN_LOCAL_BOUNDS=y enabled, we
observe a runtime panic while running Android's Compatibility Test
Suite's (CTS) android.hardware.input.cts.tests.  This is stemming from a
strlen() call in hidinput_allocate().

__compiletime_strlen is implemented in terms of __builtin_object_size(),
then does an array access to check for NUL-termination. A quirk of
__builtin_object_size() is that for strings whose values are runtime
dependent, __builtin_object_size(str, 1 or 0) returns the maximum size
of possible values when those sizes are determinable at compile time.
Example:

  static const char *v = "FOO BAR";
  static const char *y = "FOO BA";
  unsigned long x (int z) {
      // Returns 8, which is:
      // max(__builtin_object_size(v, 1), __builtin_object_size(y, 1))
      return __builtin_object_size(z ? v : y, 1);
  }

So when FORTIFY is enabled, the current implementation of
__compiletime_strlen will try to access beyond the end of y at runtime
using the size of v. Mixed with UBSAN_LOCAL_BOUNDS we get a fault.

hidinput_allocate() has a local C string whose value is control flow
dependent on a switch statement, so __builtin_object_size(str, 1)
evaluates to the maximum string length, making all other cases fault on
the last character check. hidinput_allocate() could be cleaned up to
avoid runtime calls to strlen() since the local variable can only have
literal values, so there's no benefit to trying to fortify the strlen
call site there.

Add a Kconfig check for __builtin_dynamic_object_size(), then use that
when available (gcc-12+, all supported versions of clang) which avoids
this surprising behavior.

Suggested-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>
---
 include/linux/fortify-string.h | 8 +++++++-
 init/Kconfig                   | 3 +++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h
index 3b401fa0f374..c5adad596a3f 100644
--- a/include/linux/fortify-string.h
+++ b/include/linux/fortify-string.h
@@ -14,11 +14,17 @@ void __read_overflow2_field(size_t avail, size_t wanted) __compiletime_warning("
 void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)");
 void __write_overflow_field(size_t avail, size_t wanted) __compiletime_warning("detected write beyond size of field (1st parameter); maybe use struct_group()?");
 
+#ifdef CONFIG_CC_HAS_BUILTIN_DYNAMIC_OBJECT_SIZE
+#define __object_size __builtin_dynamic_object_size
+#else
+#define __object_size __builtin_object_size
+#endif
+
 #define __compiletime_strlen(p)					\
 ({								\
 	unsigned char *__p = (unsigned char *)(p);		\
 	size_t __ret = (size_t)-1;				\
-	size_t __p_size = __builtin_object_size(p, 1);		\
+	size_t __p_size = __object_size(p, 1);			\
 	if (__p_size != (size_t)-1) {				\
 		size_t __p_len = __p_size - 1;			\
 		if (__builtin_constant_p(__p[__p_len]) &&	\
diff --git a/init/Kconfig b/init/Kconfig
index 532362fcfe31..87dd31aa54ad 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -876,6 +876,9 @@ config ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
 config CC_HAS_INT128
 	def_bool !$(cc-option,$(m64-flag) -D__SIZEOF_INT128__=0) && 64BIT
 
+config CC_HAS_BUILTIN_DYNAMIC_OBJECT_SIZE
+	def_bool !CC_IS_GCC || GCC_VERSION >= 120000
+
 config CC_IMPLICIT_FALLTHROUGH
 	string
 	default "-Wimplicit-fallthrough=5" if CC_IS_GCC && $(cc-option,-Wimplicit-fallthrough=5)
-- 
2.37.2.672.g94769d06f0-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/3] fortify: cosmetic cleanups to __compiletime_strlen
  2022-08-30 20:53 [PATCH 0/3] Fix FORTIFY=y UBSAN_LOCAL_BOUNDS=y Nick Desaulniers
  2022-08-30 20:53 ` [PATCH 1/3] fortify: use __builtin_dynamic_object_size in __compiletime_strlen Nick Desaulniers
@ 2022-08-30 20:53 ` Nick Desaulniers
  2022-08-31 13:13   ` kernel test robot
  2022-08-31 19:06   ` Kees Cook
  2022-08-30 20:53 ` [PATCH 3/3] HID: avoid runtime call to strlen Nick Desaulniers
  2 siblings, 2 replies; 8+ messages in thread
From: Nick Desaulniers @ 2022-08-30 20:53 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Tom Rix, linux-hardening, linux-kernel, llvm,
	Jiri Kosina, Benjamin Tissoires, linux-input, Masahiro Yamada,
	Nick Desaulniers

Two things I noticed in __compiletime_strlen:
1. A temporary, __p, is created+used to avoid repeated side effects from
   multiple evaluation of the macro parameter, but the macro parameter
   was being used accidentally in __builtin_object_size.
2. The temporary has a curious signedness and const-less qualification.
   Just use __auto_type.
3. (size_t)-1 is perhaps more readable as -1UL.
4. __p_size == -1UL when __builtin_object_size can't evaluate the
   object size at compile time. We could just reuse __ret and use one
   less variable here.

Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>
---
 include/linux/fortify-string.h | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h
index c5adad596a3f..aaf73575050f 100644
--- a/include/linux/fortify-string.h
+++ b/include/linux/fortify-string.h
@@ -22,11 +22,10 @@ void __write_overflow_field(size_t avail, size_t wanted) __compiletime_warning("
 
 #define __compiletime_strlen(p)					\
 ({								\
-	unsigned char *__p = (unsigned char *)(p);		\
-	size_t __ret = (size_t)-1;				\
-	size_t __p_size = __object_size(p, 1);			\
-	if (__p_size != (size_t)-1) {				\
-		size_t __p_len = __p_size - 1;			\
+	__auto_type __p = (p);					\
+	size_t __ret = __object_size(__p, 1);			\
+	if (__ret != -1UL) {					\
+		size_t __p_len = __ret - 1;			\
 		if (__builtin_constant_p(__p[__p_len]) &&	\
 		    __p[__p_len] == '\0')			\
 			__ret = __builtin_strlen(__p);		\
-- 
2.37.2.672.g94769d06f0-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/3] HID: avoid runtime call to strlen
  2022-08-30 20:53 [PATCH 0/3] Fix FORTIFY=y UBSAN_LOCAL_BOUNDS=y Nick Desaulniers
  2022-08-30 20:53 ` [PATCH 1/3] fortify: use __builtin_dynamic_object_size in __compiletime_strlen Nick Desaulniers
  2022-08-30 20:53 ` [PATCH 2/3] fortify: cosmetic cleanups to __compiletime_strlen Nick Desaulniers
@ 2022-08-30 20:53 ` Nick Desaulniers
  2022-08-31  6:05   ` Greg KH
  2 siblings, 1 reply; 8+ messages in thread
From: Nick Desaulniers @ 2022-08-30 20:53 UTC (permalink / raw)
  To: Kees Cook
  Cc: Nathan Chancellor, Tom Rix, linux-hardening, linux-kernel, llvm,
	Jiri Kosina, Benjamin Tissoires, linux-input, Masahiro Yamada,
	Nick Desaulniers

While looking into a CONFIG_FORTIFY=y related bug, I noticed that
hid_allocate calls strlen() on a local C string variable. This variable
can only have literal string values. There is no benefit to having
FORTIFY have this be a checked strlen call, because these are literal
values.  By calling strlen() explicitly in the branches of a switch, the
compiler can evaluate strlen("literal value") at compile time, rather
than at runtime.

Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>
---
 drivers/hid/hid-input.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 48c1c02c69f4..9ad3cc88c26b 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1922,12 +1922,15 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
 		switch (application) {
 		case HID_GD_KEYBOARD:
 			suffix = "Keyboard";
+			suffix_len = strlen(suffix);
 			break;
 		case HID_GD_KEYPAD:
 			suffix = "Keypad";
+			suffix_len = strlen(suffix);
 			break;
 		case HID_GD_MOUSE:
 			suffix = "Mouse";
+			suffix_len = strlen(suffix);
 			break;
 		case HID_DG_PEN:
 			/*
@@ -1938,36 +1941,44 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
 			 * will have to change it and the test suite will not be happy.
 			 */
 			suffix = "Stylus";
+			suffix_len = strlen(suffix);
 			break;
 		case HID_DG_STYLUS:
 			suffix = "Pen";
+			suffix_len = strlen(suffix);
 			break;
 		case HID_DG_TOUCHSCREEN:
 			suffix = "Touchscreen";
+			suffix_len = strlen(suffix);
 			break;
 		case HID_DG_TOUCHPAD:
 			suffix = "Touchpad";
+			suffix_len = strlen(suffix);
 			break;
 		case HID_GD_SYSTEM_CONTROL:
 			suffix = "System Control";
+			suffix_len = strlen(suffix);
 			break;
 		case HID_CP_CONSUMER_CONTROL:
 			suffix = "Consumer Control";
+			suffix_len = strlen(suffix);
 			break;
 		case HID_GD_WIRELESS_RADIO_CTLS:
 			suffix = "Wireless Radio Control";
+			suffix_len = strlen(suffix);
 			break;
 		case HID_GD_SYSTEM_MULTIAXIS:
 			suffix = "System Multi Axis";
+			suffix_len = strlen(suffix);
 			break;
 		default:
+			suffix_len = 0;
 			break;
 		}
 	}
 
 	if (suffix) {
 		name_len = strlen(hid->name);
-		suffix_len = strlen(suffix);
 		if ((name_len < suffix_len) ||
 		    strcmp(hid->name + name_len - suffix_len, suffix)) {
 			hidinput->name = kasprintf(GFP_KERNEL, "%s %s",
-- 
2.37.2.672.g94769d06f0-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/3] HID: avoid runtime call to strlen
  2022-08-30 20:53 ` [PATCH 3/3] HID: avoid runtime call to strlen Nick Desaulniers
@ 2022-08-31  6:05   ` Greg KH
  0 siblings, 0 replies; 8+ messages in thread
From: Greg KH @ 2022-08-31  6:05 UTC (permalink / raw)
  To: Nick Desaulniers
  Cc: Kees Cook, Nathan Chancellor, Tom Rix, linux-hardening,
	linux-kernel, llvm, Jiri Kosina, Benjamin Tissoires, linux-input,
	Masahiro Yamada

On Tue, Aug 30, 2022 at 01:53:09PM -0700, Nick Desaulniers wrote:
> While looking into a CONFIG_FORTIFY=y related bug, I noticed that
> hid_allocate calls strlen() on a local C string variable. This variable
> can only have literal string values. There is no benefit to having
> FORTIFY have this be a checked strlen call, because these are literal
> values.  By calling strlen() explicitly in the branches of a switch, the
> compiler can evaluate strlen("literal value") at compile time, rather
> than at runtime.
> 
> Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>
> ---
>  drivers/hid/hid-input.c | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
> index 48c1c02c69f4..9ad3cc88c26b 100644
> --- a/drivers/hid/hid-input.c
> +++ b/drivers/hid/hid-input.c
> @@ -1922,12 +1922,15 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
>  		switch (application) {
>  		case HID_GD_KEYBOARD:
>  			suffix = "Keyboard";
> +			suffix_len = strlen(suffix);
>  			break;
>  		case HID_GD_KEYPAD:
>  			suffix = "Keypad";
> +			suffix_len = strlen(suffix);
>  			break;
>  		case HID_GD_MOUSE:
>  			suffix = "Mouse";
> +			suffix_len = strlen(suffix);

<snip>

This seems ripe for someone to come along and go "look at this cleanup
patch where I move all of this duplicated code to below it in one line!"

As this is a compiler bug, why not fix the compiler?  Or at least put a
comment in here saying why this looks so odd to prevent it from being
changed in the future.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] fortify: cosmetic cleanups to __compiletime_strlen
  2022-08-30 20:53 ` [PATCH 2/3] fortify: cosmetic cleanups to __compiletime_strlen Nick Desaulniers
@ 2022-08-31 13:13   ` kernel test robot
  2022-08-31 19:06   ` Kees Cook
  1 sibling, 0 replies; 8+ messages in thread
From: kernel test robot @ 2022-08-31 13:13 UTC (permalink / raw)
  To: Nick Desaulniers, Kees Cook
  Cc: kbuild-all, Nathan Chancellor, Tom Rix, linux-hardening,
	linux-kernel, llvm, Jiri Kosina, Benjamin Tissoires, linux-input,
	Masahiro Yamada, Nick Desaulniers

Hi Nick,

I love your patch! Perhaps something to improve:

[auto build test WARNING on kees/for-next/hardening]
[also build test WARNING on kees/for-next/pstore hid/for-next linus/master v6.0-rc3 next-20220831]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Nick-Desaulniers/Fix-FORTIFY-y-UBSAN_LOCAL_BOUNDS-y/20220831-045536
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
config: x86_64-randconfig-a004 (https://download.01.org/0day-ci/archive/20220831/202208312049.JxYsAiD2-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/ab66e7d1d8a90f6addac0da9b3ae13d77f095f76
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Nick-Desaulniers/Fix-FORTIFY-y-UBSAN_LOCAL_BOUNDS-y/20220831-045536
        git checkout ab66e7d1d8a90f6addac0da9b3ae13d77f095f76
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/mtd/ubi/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/mtd/ubi/cdev.c: In function 'rename_volumes':
>> drivers/mtd/ubi/cdev.c:710:61: warning: array subscript -1 is below array bounds of 'struct <anonymous>[32]' [-Warray-bounds]
     710 |                         if (req->ents[i].vol_id == req->ents[n].vol_id) {
         |                                                    ~~~~~~~~~^~~
   In file included from drivers/mtd/ubi/cdev.c:33:
   include/uapi/mtd/ubi-user.h:404:11: note: while referencing 'ents'
     404 |         } ents[UBI_MAX_RNVOL];
         |           ^~~~
   drivers/mtd/ubi/cdev.c:715:68: warning: array subscript -1 is below array bounds of 'struct <anonymous>[32]' [-Warray-bounds]
     715 |                         if (!strcmp(req->ents[i].name, req->ents[n].name)) {
         |                                                        ~~~~~~~~~~~~^~~~~
   In file included from drivers/mtd/ubi/cdev.c:33:
   include/uapi/mtd/ubi-user.h:404:11: note: while referencing 'ents'
     404 |         } ents[UBI_MAX_RNVOL];
         |           ^~~~


vim +710 drivers/mtd/ubi/cdev.c

801c135ce73d5d Artem B. Bityutskiy 2006-06-27  668  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  669  /**
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  670   * rename_volumes - rename UBI volumes.
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  671   * @ubi: UBI device description object
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  672   * @req: volumes re-name request
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  673   *
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  674   * This is a helper function for the volume re-name IOCTL which validates the
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  675   * the request, opens the volume and calls corresponding volumes management
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  676   * function. Returns zero in case of success and a negative error code in case
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  677   * of failure.
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  678   */
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  679  static int rename_volumes(struct ubi_device *ubi,
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  680  			  struct ubi_rnvol_req *req)
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  681  {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  682  	int i, n, err;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  683  	struct list_head rename_list;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  684  	struct ubi_rename_entry *re, *re1;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  685  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  686  	if (req->count < 0 || req->count > UBI_MAX_RNVOL)
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  687  		return -EINVAL;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  688  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  689  	if (req->count == 0)
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  690  		return 0;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  691  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  692  	/* Validate volume IDs and names in the request */
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  693  	for (i = 0; i < req->count; i++) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  694  		if (req->ents[i].vol_id < 0 ||
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  695  		    req->ents[i].vol_id >= ubi->vtbl_slots)
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  696  			return -EINVAL;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  697  		if (req->ents[i].name_len < 0)
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  698  			return -EINVAL;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  699  		if (req->ents[i].name_len > UBI_VOL_NAME_MAX)
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  700  			return -ENAMETOOLONG;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  701  		req->ents[i].name[req->ents[i].name_len] = '\0';
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  702  		n = strlen(req->ents[i].name);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  703  		if (n != req->ents[i].name_len)
7fbbd05799976c Dan Carpenter       2014-09-19  704  			return -EINVAL;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  705  	}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  706  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  707  	/* Make sure volume IDs and names are unique */
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  708  	for (i = 0; i < req->count - 1; i++) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  709  		for (n = i + 1; n < req->count; n++) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13 @710  			if (req->ents[i].vol_id == req->ents[n].vol_id) {
326087033108e7 Tanya Brokhman      2014-10-20  711  				ubi_err(ubi, "duplicated volume id %d",
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  712  					req->ents[i].vol_id);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  713  				return -EINVAL;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  714  			}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  715  			if (!strcmp(req->ents[i].name, req->ents[n].name)) {
326087033108e7 Tanya Brokhman      2014-10-20  716  				ubi_err(ubi, "duplicated volume name \"%s\"",
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  717  					req->ents[i].name);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  718  				return -EINVAL;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  719  			}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  720  		}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  721  	}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  722  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  723  	/* Create the re-name list */
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  724  	INIT_LIST_HEAD(&rename_list);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  725  	for (i = 0; i < req->count; i++) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  726  		int vol_id = req->ents[i].vol_id;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  727  		int name_len = req->ents[i].name_len;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  728  		const char *name = req->ents[i].name;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  729  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  730  		re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  731  		if (!re) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  732  			err = -ENOMEM;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  733  			goto out_free;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  734  		}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  735  
892abde56c1c5a Richard Weinberger  2014-11-24  736  		re->desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_METAONLY);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  737  		if (IS_ERR(re->desc)) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  738  			err = PTR_ERR(re->desc);
326087033108e7 Tanya Brokhman      2014-10-20  739  			ubi_err(ubi, "cannot open volume %d, error %d",
326087033108e7 Tanya Brokhman      2014-10-20  740  				vol_id, err);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  741  			kfree(re);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  742  			goto out_free;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  743  		}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  744  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  745  		/* Skip this re-naming if the name does not really change */
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  746  		if (re->desc->vol->name_len == name_len &&
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  747  		    !memcmp(re->desc->vol->name, name, name_len)) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  748  			ubi_close_volume(re->desc);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  749  			kfree(re);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  750  			continue;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  751  		}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  752  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  753  		re->new_name_len = name_len;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  754  		memcpy(re->new_name, name, name_len);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  755  		list_add_tail(&re->list, &rename_list);
719bb84017fcfc Artem Bityutskiy    2012-08-27  756  		dbg_gen("will rename volume %d from \"%s\" to \"%s\"",
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  757  			vol_id, re->desc->vol->name, name);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  758  	}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  759  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  760  	if (list_empty(&rename_list))
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  761  		return 0;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  762  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  763  	/* Find out the volumes which have to be removed */
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  764  	list_for_each_entry(re, &rename_list, list) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  765  		struct ubi_volume_desc *desc;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  766  		int no_remove_needed = 0;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  767  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  768  		/*
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  769  		 * Volume @re->vol_id is going to be re-named to
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  770  		 * @re->new_name, while its current name is @name. If a volume
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  771  		 * with name @re->new_name currently exists, it has to be
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  772  		 * removed, unless it is also re-named in the request (@req).
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  773  		 */
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  774  		list_for_each_entry(re1, &rename_list, list) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  775  			if (re->new_name_len == re1->desc->vol->name_len &&
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  776  			    !memcmp(re->new_name, re1->desc->vol->name,
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  777  				    re1->desc->vol->name_len)) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  778  				no_remove_needed = 1;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  779  				break;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  780  			}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  781  		}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  782  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  783  		if (no_remove_needed)
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  784  			continue;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  785  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  786  		/*
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  787  		 * It seems we need to remove volume with name @re->new_name,
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  788  		 * if it exists.
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  789  		 */
f2863c54f30ccc Artem Bityutskiy    2008-12-28  790  		desc = ubi_open_volume_nm(ubi->ubi_num, re->new_name,
f2863c54f30ccc Artem Bityutskiy    2008-12-28  791  					  UBI_EXCLUSIVE);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  792  		if (IS_ERR(desc)) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  793  			err = PTR_ERR(desc);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  794  			if (err == -ENODEV)
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  795  				/* Re-naming into a non-existing volume name */
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  796  				continue;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  797  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  798  			/* The volume exists but busy, or an error occurred */
326087033108e7 Tanya Brokhman      2014-10-20  799  			ubi_err(ubi, "cannot open volume \"%s\", error %d",
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  800  				re->new_name, err);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  801  			goto out_free;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  802  		}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  803  
01ebc12f5f2e88 Julia Lawall        2010-08-07  804  		re1 = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL);
01ebc12f5f2e88 Julia Lawall        2010-08-07  805  		if (!re1) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  806  			err = -ENOMEM;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  807  			ubi_close_volume(desc);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  808  			goto out_free;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  809  		}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  810  
01ebc12f5f2e88 Julia Lawall        2010-08-07  811  		re1->remove = 1;
01ebc12f5f2e88 Julia Lawall        2010-08-07  812  		re1->desc = desc;
01ebc12f5f2e88 Julia Lawall        2010-08-07  813  		list_add(&re1->list, &rename_list);
719bb84017fcfc Artem Bityutskiy    2012-08-27  814  		dbg_gen("will remove volume %d, name \"%s\"",
01ebc12f5f2e88 Julia Lawall        2010-08-07  815  			re1->desc->vol->vol_id, re1->desc->vol->name);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  816  	}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  817  
f089c0b28cdba1 Artem Bityutskiy    2009-05-07  818  	mutex_lock(&ubi->device_mutex);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  819  	err = ubi_rename_volumes(ubi, &rename_list);
f089c0b28cdba1 Artem Bityutskiy    2009-05-07  820  	mutex_unlock(&ubi->device_mutex);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  821  
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  822  out_free:
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  823  	list_for_each_entry_safe(re, re1, &rename_list, list) {
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  824  		ubi_close_volume(re->desc);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  825  		list_del(&re->list);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  826  		kfree(re);
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  827  	}
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  828  	return err;
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  829  }
f40ac9cdf69912 Artem Bityutskiy    2008-07-13  830  

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/3] fortify: use __builtin_dynamic_object_size in __compiletime_strlen
  2022-08-30 20:53 ` [PATCH 1/3] fortify: use __builtin_dynamic_object_size in __compiletime_strlen Nick Desaulniers
@ 2022-08-31 18:34   ` Kees Cook
  0 siblings, 0 replies; 8+ messages in thread
From: Kees Cook @ 2022-08-31 18:34 UTC (permalink / raw)
  To: Nick Desaulniers
  Cc: Nathan Chancellor, Tom Rix, linux-hardening, linux-kernel, llvm,
	Jiri Kosina, Benjamin Tissoires, linux-input, Masahiro Yamada

On Tue, Aug 30, 2022 at 01:53:07PM -0700, Nick Desaulniers wrote:
> With CONFIG_FORTIFY=y and CONFIG_UBSAN_LOCAL_BOUNDS=y enabled, we
> observe a runtime panic while running Android's Compatibility Test
> Suite's (CTS) android.hardware.input.cts.tests.  This is stemming from a
> strlen() call in hidinput_allocate().
> 
> __compiletime_strlen is implemented in terms of __builtin_object_size(),
> then does an array access to check for NUL-termination. A quirk of
> __builtin_object_size() is that for strings whose values are runtime
> dependent, __builtin_object_size(str, 1 or 0) returns the maximum size
> of possible values when those sizes are determinable at compile time.
> Example:
> 
>   static const char *v = "FOO BAR";
>   static const char *y = "FOO BA";
>   unsigned long x (int z) {
>       // Returns 8, which is:
>       // max(__builtin_object_size(v, 1), __builtin_object_size(y, 1))
>       return __builtin_object_size(z ? v : y, 1);
>   }
> 
> So when FORTIFY is enabled, the current implementation of
> __compiletime_strlen will try to access beyond the end of y at runtime
> using the size of v. Mixed with UBSAN_LOCAL_BOUNDS we get a fault.
> 
> hidinput_allocate() has a local C string whose value is control flow
> dependent on a switch statement, so __builtin_object_size(str, 1)
> evaluates to the maximum string length, making all other cases fault on
> the last character check. hidinput_allocate() could be cleaned up to
> avoid runtime calls to strlen() since the local variable can only have
> literal values, so there's no benefit to trying to fortify the strlen
> call site there.
> 
> Add a Kconfig check for __builtin_dynamic_object_size(), then use that
> when available (gcc-12+, all supported versions of clang) which avoids
> this surprising behavior.
> 
> Suggested-by: Kees Cook <keescook@chromium.org>
> Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>
> ---
>  include/linux/fortify-string.h | 8 +++++++-
>  init/Kconfig                   | 3 +++
>  2 files changed, 10 insertions(+), 1 deletion(-)
> 
> diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h
> index 3b401fa0f374..c5adad596a3f 100644
> --- a/include/linux/fortify-string.h
> +++ b/include/linux/fortify-string.h
> @@ -14,11 +14,17 @@ void __read_overflow2_field(size_t avail, size_t wanted) __compiletime_warning("
>  void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)");
>  void __write_overflow_field(size_t avail, size_t wanted) __compiletime_warning("detected write beyond size of field (1st parameter); maybe use struct_group()?");
>  
> +#ifdef CONFIG_CC_HAS_BUILTIN_DYNAMIC_OBJECT_SIZE
> +#define __object_size __builtin_dynamic_object_size
> +#else
> +#define __object_size __builtin_object_size
> +#endif

Instead of a Kconfig, how about just:

#if __has_builtin(__builtin_dynamic_object_size)
# define __object_size __builtin_dynamic_object_size
#else
# define __object_size __builtin_object_size
#endif

?

> +
>  #define __compiletime_strlen(p)					\
>  ({								\
>  	unsigned char *__p = (unsigned char *)(p);		\
>  	size_t __ret = (size_t)-1;				\
> -	size_t __p_size = __builtin_object_size(p, 1);		\
> +	size_t __p_size = __object_size(p, 1);			\
>  	if (__p_size != (size_t)-1) {				\
>  		size_t __p_len = __p_size - 1;			\
>  		if (__builtin_constant_p(__p[__p_len]) &&	\

The fact that __builtin_object_size() will actually span control flow,
and produce a size-inclusive result on the possible inputs is ...
surprising and potentially quite problematic. But I'm satisfied that
bdos appears to fix it here (since the "compiletime"ness will still get
filtered by the __builtin_constant_p() check).

-Kees

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/3] fortify: cosmetic cleanups to __compiletime_strlen
  2022-08-30 20:53 ` [PATCH 2/3] fortify: cosmetic cleanups to __compiletime_strlen Nick Desaulniers
  2022-08-31 13:13   ` kernel test robot
@ 2022-08-31 19:06   ` Kees Cook
  1 sibling, 0 replies; 8+ messages in thread
From: Kees Cook @ 2022-08-31 19:06 UTC (permalink / raw)
  To: Nick Desaulniers
  Cc: Nathan Chancellor, Tom Rix, linux-hardening, linux-kernel, llvm,
	Jiri Kosina, Benjamin Tissoires, linux-input, Masahiro Yamada

On Tue, Aug 30, 2022 at 01:53:08PM -0700, Nick Desaulniers wrote:
> Two things I noticed in __compiletime_strlen:

Four? :)

> 1. A temporary, __p, is created+used to avoid repeated side effects from
>    multiple evaluation of the macro parameter, but the macro parameter
>    was being used accidentally in __builtin_object_size.

__builtin_object_size(), like sizeof() but unlike __builtin_strlen(),
will not evaluate side-effects: https://godbolt.org/z/Yaa1z7YvK
And using bos on __p will sometimes mask the actual object, so p needs to
stay the argument.

> 2. The temporary has a curious signedness and const-less qualification.
>    Just use __auto_type.

__auto_type is pretty rare in the kernel, but does provide the removal
of "const". Even though the kernel builds with -Wno-pointer-sign, the
explicit case does fix a potential warnings about signedness differences,
not just const differences, for __builtin_strlen() which requires "const
char *", but many arguments are "unsigned char *", "u8 *", etc.

Is __auto_type more readable than the explicit cast? It does seem to
work fine.

> 3. (size_t)-1 is perhaps more readable as -1UL.

That's true, though I kind of prefer (size_t)-1, though yes, it appears
to be the extreme minority in the kernel.

> 4. __p_size == -1UL when __builtin_object_size can't evaluate the
>    object size at compile time. We could just reuse __ret and use one
>    less variable here.

This seems to get entire optimized away by the compiler? I think it's
more readable to keep the explicit variable.

-Kees

> 
> Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>
> ---
>  include/linux/fortify-string.h | 9 ++++-----
>  1 file changed, 4 insertions(+), 5 deletions(-)
> 
> diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h
> index c5adad596a3f..aaf73575050f 100644
> --- a/include/linux/fortify-string.h
> +++ b/include/linux/fortify-string.h
> @@ -22,11 +22,10 @@ void __write_overflow_field(size_t avail, size_t wanted) __compiletime_warning("
>  
>  #define __compiletime_strlen(p)					\
>  ({								\
> -	unsigned char *__p = (unsigned char *)(p);		\
> -	size_t __ret = (size_t)-1;				\
> -	size_t __p_size = __object_size(p, 1);			\
> -	if (__p_size != (size_t)-1) {				\
> -		size_t __p_len = __p_size - 1;			\
> +	__auto_type __p = (p);					\
> +	size_t __ret = __object_size(__p, 1);			\
> +	if (__ret != -1UL) {					\
> +		size_t __p_len = __ret - 1;			\
>  		if (__builtin_constant_p(__p[__p_len]) &&	\
>  		    __p[__p_len] == '\0')			\
>  			__ret = __builtin_strlen(__p);		\
> -- 
> 2.37.2.672.g94769d06f0-goog
> 

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2022-08-31 19:06 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-08-30 20:53 [PATCH 0/3] Fix FORTIFY=y UBSAN_LOCAL_BOUNDS=y Nick Desaulniers
2022-08-30 20:53 ` [PATCH 1/3] fortify: use __builtin_dynamic_object_size in __compiletime_strlen Nick Desaulniers
2022-08-31 18:34   ` Kees Cook
2022-08-30 20:53 ` [PATCH 2/3] fortify: cosmetic cleanups to __compiletime_strlen Nick Desaulniers
2022-08-31 13:13   ` kernel test robot
2022-08-31 19:06   ` Kees Cook
2022-08-30 20:53 ` [PATCH 3/3] HID: avoid runtime call to strlen Nick Desaulniers
2022-08-31  6:05   ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).