[binutils-gdb] alpha, rl78, rx: UB in reloc handling

Alan Modra via Binutils-cvs <[email protected]> Tue, 7 Jul 2026 01:13:11 +0000 (GMT)
Newsgroups gmane.comp.gnu.binutils.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=192a958f5725c0f5a0338ceb9de580b540db6adb

commit 192a958f5725c0f5a0338ceb9de580b540db6adb
Author: Alan Modra <[email protected]>
Date:   Tue Jul 7 10:08:18 2026 +0930

    alpha, rl78, rx: UB in reloc handling
    
    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.

Diff:
---
 bfd/coff-alpha.c | 10 +++++--
 bfd/elf32-rl78.c | 36 +++++++++++++++++---------
 bfd/elf32-rx.c   | 79 +++++++++++++++++++++++++++++++++++++++++++-------------
 3 files changed, 93 insertions(+), 32 deletions(-)

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;