alpha, rl78, rx: UB in reloc handling

Message ID akxSjQM3zALzWU-3@squeak.grove.modra.org
State New
Headers
Series alpha, rl78, rx: UB in reloc handling |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply

Commit Message

Alan Modra July 7, 2026, 1:12 a.m. UTC
  This patch avoids undefined behaviour and divide by zero exceptions in
some relocation processing.  In most cases, unsigned arithmetic is
used which has defined overflow characteristics.  Arithmetic right
shift, division, and modulo operations have more special cases.  See
the explanation in commit 30200464e9dd.

	* coff-alpha.c (alpha_ecoff_get_relocated_section_contents): Avoid
	UB in RSHIFT reloc.
	(alpha_relocate_section): Likewise.
	* elf32-rl78.c (rl78_compute_complex_reloc): Avoid UB in reloc
	arithmetic.
	* elf32-rx.c (rx_elf_relocate_section): Likewise.
	(rx_offset_for_reloc): Likewise.
  

Patch

diff --git a/bfd/coff-alpha.c b/bfd/coff-alpha.c
index 35677e9a933..f0823cdcaa9 100644
--- a/bfd/coff-alpha.c
+++ b/bfd/coff-alpha.c
@@ -1128,7 +1128,10 @@  alpha_ecoff_get_relocated_section_contents (bfd *abfd,
 		break;
 	      }
 
-	    stack[tos - 1] >>= relocation;
+	    if (relocation >= 64)
+	      stack[tos - 1] = 0;
+	    else
+	      stack[tos - 1] >>= relocation;
 	  }
 	  break;
 
@@ -1755,7 +1758,10 @@  alpha_relocate_section (bfd *output_bfd,
 		      r = bfd_reloc_notsupported;
 		      break;
 		    }
-		  stack[tos - 1] >>= addend;
+		  if (addend >= 64)
+		    stack[tos - 1] = 0;
+		  else
+		    stack[tos - 1] >>= addend;
 		  break;
 		}
 	    }
diff --git a/bfd/elf32-rl78.c b/bfd/elf32-rl78.c
index 2b782c1463a..e9e047fcca7 100644
--- a/bfd/elf32-rl78.c
+++ b/bfd/elf32-rl78.c
@@ -442,14 +442,14 @@  rl78_compute_complex_reloc (unsigned long  r_type,
 
     case R_RL78_OPneg:
       tmp1 = rl78_stack_pop (&status);
-      tmp1 = - tmp1;
+      tmp1 = -(uint32_t) tmp1;
       rl78_stack_push (tmp1, &status);
       break;
 
     case R_RL78_OPadd:
       tmp2 = rl78_stack_pop (&status);
       tmp1 = rl78_stack_pop (&status);
-      tmp1 += tmp2;
+      tmp1 += (uint32_t) tmp2;
       rl78_stack_push (tmp1, &status);
       break;
 
@@ -458,41 +458,51 @@  rl78_compute_complex_reloc (unsigned long  r_type,
 	 then B, then OPSUB.  So the first op we pop is B, not A.  */
       tmp2 = rl78_stack_pop (&status);	/* B */
       tmp1 = rl78_stack_pop (&status);	/* A */
-      tmp1 -= tmp2;		/* A - B */
+      tmp1 -= (uint32_t) tmp2;		/* A - B */
       rl78_stack_push (tmp1, &status);
       break;
 
     case R_RL78_OPmul:
       tmp2 = rl78_stack_pop (&status);
       tmp1 = rl78_stack_pop (&status);
-      tmp1 *= tmp2;
+      tmp1 *= (uint32_t) tmp2;
       rl78_stack_push (tmp1, &status);
       break;
 
     case R_RL78_OPdiv:
       tmp2 = rl78_stack_pop (&status);
       tmp1 = rl78_stack_pop (&status);
-      if (tmp2 != 0)
-	tmp1 /= tmp2;
-      else
+      if (tmp2 == 0)
 	{
 	  tmp1 = 0;
 	  status = bfd_reloc_overflow;
 	}
+      else if (tmp2 == 1)
+	;
+      else if (tmp2 == -1)
+	tmp1 = -(uint32_t) tmp1;
+      else
+	tmp1 /= tmp2;
       rl78_stack_push (tmp1, &status);
       break;
 
     case R_RL78_OPshla:
       tmp2 = rl78_stack_pop (&status);
       tmp1 = rl78_stack_pop (&status);
-      tmp1 <<= tmp2;
+      if ((uint32_t) tmp2 >= 32)
+	tmp1 = 0;
+      else
+	tmp1 = (uint32_t) tmp1 << tmp2;
       rl78_stack_push (tmp1, &status);
       break;
 
     case R_RL78_OPshra:
       tmp2 = rl78_stack_pop (&status);
       tmp1 = rl78_stack_pop (&status);
-      tmp1 >>= tmp2;
+      if ((uint32_t) tmp2 >= 31)
+	tmp1 = tmp1 < 0 ? -1 : 1;
+      else
+	tmp1 >>= tmp2;
       rl78_stack_push (tmp1, &status);
       break;
 
@@ -534,13 +544,15 @@  rl78_compute_complex_reloc (unsigned long  r_type,
     case R_RL78_OPmod:
       tmp2 = rl78_stack_pop (&status);
       tmp1 = rl78_stack_pop (&status);
-      if (tmp2 != 0)
-	tmp1 %= tmp2;
-      else
+      if (tmp2 == 0)
 	{
 	  tmp1 = 0;
 	  status = bfd_reloc_overflow;
 	}
+      else if (tmp2 == 1 || tmp2 == -1)
+	tmp1 = 0;
+      else
+	tmp1 %= tmp2;
       rl78_stack_push (tmp1, &status);
       break;
     }
diff --git a/bfd/elf32-rx.c b/bfd/elf32-rx.c
index 1421e443e51..68eb99fd392 100644
--- a/bfd/elf32-rx.c
+++ b/bfd/elf32-rx.c
@@ -1308,7 +1308,7 @@  rx_elf_relocate_section
 
 	case R_RX_OPneg:
 	  {
-	    int32_t tmp;
+	    uint32_t tmp;
 
 	    saw_subtract = true;
 	    RX_STACK_POP (tmp);
@@ -1319,7 +1319,7 @@  rx_elf_relocate_section
 
 	case R_RX_OPadd:
 	  {
-	    int32_t tmp1, tmp2;
+	    uint32_t tmp1, tmp2;
 
 	    RX_STACK_POP (tmp1);
 	    RX_STACK_POP (tmp2);
@@ -1330,7 +1330,7 @@  rx_elf_relocate_section
 
 	case R_RX_OPsub:
 	  {
-	    int32_t tmp1, tmp2;
+	    uint32_t tmp1, tmp2;
 
 	    saw_subtract = true;
 	    RX_STACK_POP (tmp1);
@@ -1342,7 +1342,7 @@  rx_elf_relocate_section
 
 	case R_RX_OPmul:
 	  {
-	    int32_t tmp1, tmp2;
+	    uint32_t tmp1, tmp2;
 
 	    RX_STACK_POP (tmp1);
 	    RX_STACK_POP (tmp2);
@@ -1357,29 +1357,46 @@  rx_elf_relocate_section
 
 	    RX_STACK_POP (tmp1);
 	    RX_STACK_POP (tmp2);
-	    tmp1 /= tmp2;
+	    if (tmp2 == 0)
+	      {
+		tmp1 = 0;
+		r = bfd_reloc_overflow;
+	      }
+	    else if (tmp2 == 1)
+	      ;
+	    else if (tmp2 == -1)
+	      tmp1 = - (uint32_t) tmp1;
+	    else
+	      tmp1 /= tmp2;
 	    RX_STACK_PUSH (tmp1);
 	  }
 	  break;
 
 	case R_RX_OPshla:
 	  {
-	    int32_t tmp1, tmp2;
+	    uint32_t tmp1, tmp2;
 
 	    RX_STACK_POP (tmp1);
 	    RX_STACK_POP (tmp2);
-	    tmp1 <<= tmp2;
+	    if (tmp2 >= 32)
+	      tmp1 = 0;
+	    else
+	      tmp1 <<= tmp2;
 	    RX_STACK_PUSH (tmp1);
 	  }
 	  break;
 
 	case R_RX_OPshra:
 	  {
-	    int32_t tmp1, tmp2;
+	    int32_t tmp1;
+	    uint32_t tmp2;
 
 	    RX_STACK_POP (tmp1);
 	    RX_STACK_POP (tmp2);
-	    tmp1 >>= tmp2;
+	    if (tmp2 >= 31)
+	      tmp1 = tmp1 < 0 ? -1 : 1;
+	    else
+	      tmp1 >>= tmp2;
 	    RX_STACK_PUSH (tmp1);
 	  }
 	  break;
@@ -1441,7 +1458,15 @@  rx_elf_relocate_section
 
 	    RX_STACK_POP (tmp1);
 	    RX_STACK_POP (tmp2);
-	    tmp1 %= tmp2;
+	    if (tmp2 == 0)
+	      {
+		tmp1 = 0;
+		r = bfd_reloc_overflow;
+	      }
+	    else if (tmp2 == 1 || tmp2 == -1)
+	      tmp1 = 0;
+	    else
+	      tmp1 %= tmp2;
 	    RX_STACK_PUSH (tmp1);
 	  }
 	  break;
@@ -1853,49 +1878,62 @@  rx_offset_for_reloc (bfd *		      abfd,
 
 	case R_RX_OPneg:
 	  RX_STACK_POP (tmp1);
-	  tmp1 = - tmp1;
+	  tmp1 = - (uint32_t) tmp1;
 	  RX_STACK_PUSH (tmp1);
 	  break;
 
 	case R_RX_OPadd:
 	  RX_STACK_POP (tmp1);
 	  RX_STACK_POP (tmp2);
-	  tmp1 += tmp2;
+	  tmp1 += (uint32_t) tmp2;
 	  RX_STACK_PUSH (tmp1);
 	  break;
 
 	case R_RX_OPsub:
 	  RX_STACK_POP (tmp1);
 	  RX_STACK_POP (tmp2);
-	  tmp2 -= tmp1;
+	  tmp2 -= (uint32_t) tmp1;
 	  RX_STACK_PUSH (tmp2);
 	  break;
 
 	case R_RX_OPmul:
 	  RX_STACK_POP (tmp1);
 	  RX_STACK_POP (tmp2);
-	  tmp1 *= tmp2;
+	  tmp1 *= (uint32_t) tmp2;
 	  RX_STACK_PUSH (tmp1);
 	  break;
 
 	case R_RX_OPdiv:
 	  RX_STACK_POP (tmp1);
 	  RX_STACK_POP (tmp2);
-	  tmp1 /= tmp2;
+	  if (tmp2 == 0)
+	    tmp1 = 0;
+	  else if (tmp2 == 1)
+	    ;
+	  else if (tmp2 == -1)
+	    tmp1 = - (uint32_t) tmp1;
+	  else
+	    tmp1 /= tmp2;
 	  RX_STACK_PUSH (tmp1);
 	  break;
 
 	case R_RX_OPshla:
 	  RX_STACK_POP (tmp1);
 	  RX_STACK_POP (tmp2);
-	  tmp1 <<= tmp2;
+	  if ((uint32_t) tmp2 >= 32)
+	    tmp1 = 0;
+	  else
+	    tmp1 = (uint32_t) tmp1 << tmp2;
 	  RX_STACK_PUSH (tmp1);
 	  break;
 
 	case R_RX_OPshra:
 	  RX_STACK_POP (tmp1);
 	  RX_STACK_POP (tmp2);
-	  tmp1 >>= tmp2;
+	  if ((uint32_t) tmp2 >= 31)
+	    tmp1 = tmp1 < 0 ? -1 : 1;
+	  else
+	    tmp1 >>= tmp2;
 	  RX_STACK_PUSH (tmp1);
 	  break;
 
@@ -1937,7 +1975,12 @@  rx_offset_for_reloc (bfd *		      abfd,
 	case R_RX_OPmod:
 	  RX_STACK_POP (tmp1);
 	  RX_STACK_POP (tmp2);
-	  tmp1 %= tmp2;
+	  if (tmp2 == 0)
+	    tmp1 = 0;
+	  else if (tmp2 == -1 || tmp2 == 1)
+	    tmp1 = 0;
+	  else
+	    tmp1 %= tmp2;
 	  RX_STACK_PUSH (tmp1);
 	  break;