[PATCH] binutils: fix out-of-bounds write in stab_xcoff_builtin_type (bz 28694)

Alan Modra amodra@gmail.com
Wed Dec 15 01:20:42 GMT 2021


On Tue, Dec 14, 2021 at 07:57:32PM +0300, Pavel Mayorov via Binutils wrote:
> I found an out-of-bounds write to the array 'info->xcoff_types' in the
> function 'stab_xcoff_builtin_type' (binutils/stabs.c).
> 
> Processing of typenum -34 results in overwriting of adjacent field
> 'info->tags' at line 3668:
>   info->xcoff_types[-typenum] = rettype;
> 
> This eventually leads to a segmentation fault due to illegal memory
> reference performed by the function 'finish_stab'. ASAN catches this
> as heap-buffer-overflow.
> 
> To solve this problem, it is enough to correct the index by which the
> array is accessed: decrease it by 1

Thanks for the patch.  I'm going to commit a variation

> +  index = -typenum - 1;

because with typenum being a signed int -typenum will overflow and
trigger ubsan warnings if typenum is INT_MIN.

	PR 28694
	* stabs.c (stab_xcoff_builtin_type): Make typenum unsigned.
	Negate typenum earlier, simplifying bounds checking.  Correct
	off-by-one indexing.  Adjust switch cases.

diff --git a/binutils/stabs.c b/binutils/stabs.c
index 274bfb0e7fa..83ee3ea5fa4 100644
--- a/binutils/stabs.c
+++ b/binutils/stabs.c
@@ -202,7 +202,7 @@ static debug_type stab_find_type (void *, struct stab_handle *, const int *);
 static bool stab_record_type
   (void *, struct stab_handle *, const int *, debug_type);
 static debug_type stab_xcoff_builtin_type
-  (void *, struct stab_handle *, int);
+  (void *, struct stab_handle *, unsigned int);
 static debug_type stab_find_tagged_type
   (void *, struct stab_handle *, const char *, int, enum debug_type_kind);
 static debug_type *stab_demangle_argtypes
@@ -3496,166 +3496,167 @@ stab_record_type (void *dhandle ATTRIBUTE_UNUSED, struct stab_handle *info,
 
 static debug_type
 stab_xcoff_builtin_type (void *dhandle, struct stab_handle *info,
-			 int typenum)
+			 unsigned int typenum)
 {
   debug_type rettype;
   const char *name;
 
-  if (typenum >= 0 || typenum < -XCOFF_TYPE_COUNT)
+  typenum = -typenum - 1;
+  if (typenum >= XCOFF_TYPE_COUNT)
     {
-      fprintf (stderr, _("Unrecognized XCOFF type %d\n"), typenum);
+      fprintf (stderr, _("Unrecognized XCOFF type %d\n"), -typenum - 1);
       return DEBUG_TYPE_NULL;
     }
-  if (info->xcoff_types[-typenum] != NULL)
-    return info->xcoff_types[-typenum];
+  if (info->xcoff_types[typenum] != NULL)
+    return info->xcoff_types[typenum];
 
-  switch (-typenum)
+  switch (typenum)
     {
-    case 1:
+    case 0:
       /* The size of this and all the other types are fixed, defined
 	 by the debugging format.  */
       name = "int";
       rettype = debug_make_int_type (dhandle, 4, false);
       break;
-    case 2:
+    case 1:
       name = "char";
       rettype = debug_make_int_type (dhandle, 1, false);
       break;
-    case 3:
+    case 2:
       name = "short";
       rettype = debug_make_int_type (dhandle, 2, false);
       break;
-    case 4:
+    case 3:
       name = "long";
       rettype = debug_make_int_type (dhandle, 4, false);
       break;
-    case 5:
+    case 4:
       name = "unsigned char";
       rettype = debug_make_int_type (dhandle, 1, true);
       break;
-    case 6:
+    case 5:
       name = "signed char";
       rettype = debug_make_int_type (dhandle, 1, false);
       break;
-    case 7:
+    case 6:
       name = "unsigned short";
       rettype = debug_make_int_type (dhandle, 2, true);
       break;
-    case 8:
+    case 7:
       name = "unsigned int";
       rettype = debug_make_int_type (dhandle, 4, true);
       break;
-    case 9:
+    case 8:
       name = "unsigned";
       rettype = debug_make_int_type (dhandle, 4, true);
       break;
-    case 10:
+    case 9:
       name = "unsigned long";
       rettype = debug_make_int_type (dhandle, 4, true);
       break;
-    case 11:
+    case 10:
       name = "void";
       rettype = debug_make_void_type (dhandle);
       break;
-    case 12:
+    case 11:
       /* IEEE single precision (32 bit).  */
       name = "float";
       rettype = debug_make_float_type (dhandle, 4);
       break;
-    case 13:
+    case 12:
       /* IEEE double precision (64 bit).  */
       name = "double";
       rettype = debug_make_float_type (dhandle, 8);
       break;
-    case 14:
+    case 13:
       /* This is an IEEE double on the RS/6000, and different machines
 	 with different sizes for "long double" should use different
 	 negative type numbers.  See stabs.texinfo.  */
       name = "long double";
       rettype = debug_make_float_type (dhandle, 8);
       break;
-    case 15:
+    case 14:
       name = "integer";
       rettype = debug_make_int_type (dhandle, 4, false);
       break;
-    case 16:
+    case 15:
       name = "boolean";
       rettype = debug_make_bool_type (dhandle, 4);
       break;
-    case 17:
+    case 16:
       name = "short real";
       rettype = debug_make_float_type (dhandle, 4);
       break;
-    case 18:
+    case 17:
       name = "real";
       rettype = debug_make_float_type (dhandle, 8);
       break;
-    case 19:
+    case 18:
       /* FIXME */
       name = "stringptr";
       rettype = NULL;
       break;
-    case 20:
+    case 19:
       /* FIXME */
       name = "character";
       rettype = debug_make_int_type (dhandle, 1, true);
       break;
-    case 21:
+    case 20:
       name = "logical*1";
       rettype = debug_make_bool_type (dhandle, 1);
       break;
-    case 22:
+    case 21:
       name = "logical*2";
       rettype = debug_make_bool_type (dhandle, 2);
       break;
-    case 23:
+    case 22:
       name = "logical*4";
       rettype = debug_make_bool_type (dhandle, 4);
       break;
-    case 24:
+    case 23:
       name = "logical";
       rettype = debug_make_bool_type (dhandle, 4);
       break;
-    case 25:
+    case 24:
       /* Complex type consisting of two IEEE single precision values.  */
       name = "complex";
       rettype = debug_make_complex_type (dhandle, 8);
       break;
-    case 26:
+    case 25:
       /* Complex type consisting of two IEEE double precision values.  */
       name = "double complex";
       rettype = debug_make_complex_type (dhandle, 16);
       break;
-    case 27:
+    case 26:
       name = "integer*1";
       rettype = debug_make_int_type (dhandle, 1, false);
       break;
-    case 28:
+    case 27:
       name = "integer*2";
       rettype = debug_make_int_type (dhandle, 2, false);
       break;
-    case 29:
+    case 28:
       name = "integer*4";
       rettype = debug_make_int_type (dhandle, 4, false);
       break;
-    case 30:
+    case 29:
       /* FIXME */
       name = "wchar";
       rettype = debug_make_int_type (dhandle, 2, false);
       break;
-    case 31:
+    case 30:
       name = "long long";
       rettype = debug_make_int_type (dhandle, 8, false);
       break;
-    case 32:
+    case 31:
       name = "unsigned long long";
       rettype = debug_make_int_type (dhandle, 8, true);
       break;
-    case 33:
+    case 32:
       name = "logical*8";
       rettype = debug_make_bool_type (dhandle, 8);
       break;
-    case 34:
+    case 33:
       name = "integer*8";
       rettype = debug_make_int_type (dhandle, 8, false);
       break;
@@ -3664,9 +3665,7 @@ stab_xcoff_builtin_type (void *dhandle, struct stab_handle *info,
     }
 
   rettype = debug_name_type (dhandle, name, rettype);
-
-  info->xcoff_types[-typenum] = rettype;
-
+  info->xcoff_types[typenum] = rettype;
   return rettype;
 }
 


-- 
Alan Modra
Australia Development Lab, IBM


More information about the Binutils mailing list