CVE-2021-40507 (published 2023-04-18T12:15Z)

An issue was discovered in the ALU unit of the OR1200 (aka OpenRISC 1200) processor 2011-09-10 through 2015-11-11. The overflow flag is not being updated correctly for the subtract instruction, which results in an incorrect value in the overflow flag. Any software that relies on this flag may experience corruption in execution.

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-287
cwe_name: Improper Authentication
description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
extended_description: 
            
			
         
         

Fix information:
repo_url: https://github.com/openrisc/or1200
commit_details: repo_url: https://github.com/openrisc/or1200
author: julius
author_date: 2011-09-01 18:49:24+00:00
msg: or1200: add carry, overflow bits, and range exception
repository: repo_url: https://github.com/openrisc/or1200
repo_name: openrisc/or1200
description: OpenRISC 1200 implementation
repo_language: Verilog
owner: openrisc
file_changes: filename: or1200_alu.v
change_type: ModificationType.MODIFY
diff: @@ -56,6 +56,7 @@ module or1200_alu(
 	alu_op, alu_op2, comp_op,
 	cust5_op, cust5_limm,
 	result, flagforw, flag_we,
+	ovforw, ov_we,
 	cyforw, cy_we, carry, flag
 );
 
@@ -78,6 +79,8 @@ output				flagforw;
 output				flag_we;
 output				cyforw;
 output				cy_we;
+output				ovforw;
+output				ov_we;
 input				carry;
 input         flag;
 
@@ -91,7 +94,10 @@ reg	[width-1:0]		result_cust5;
 reg				flagforw;
 reg				flagcomp;
 reg				flag_we;
+reg				cyforw;
 reg				cy_we;
+reg				ovforw;
+reg				ov_we;   
 wire	[width-1:0]		comp_a;
 wire	[width-1:0]		comp_b;
 `ifdef OR1200_IMPL_ALU_COMP1
@@ -99,16 +105,17 @@ wire				a_eq_b;
 wire				a_lt_b;
 `endif
 wire	[width-1:0]		result_sum;
-`ifdef OR1200_IMPL_ADDC
-wire	[width-1:0]		result_csum;
-wire				cy_csum;
-`endif
 wire	[width-1:0]		result_and;
 wire				cy_sum;
 `ifdef OR1200_IMPL_SUB
 wire				cy_sub;
 `endif
-reg				cyforw;
+wire    			ov_sum;
+wire    [width-1:0] 		carry_in;
+
+wire    [width-1:0]		b_mux;
+   
+   
 
 //
 // Combinatorial logic
@@ -120,12 +127,24 @@ assign a_eq_b = (comp_a == comp_b);
 assign a_lt_b = (comp_a < comp_b);
 `endif
 `ifdef OR1200_IMPL_SUB
-assign cy_sub = a < b;
+assign cy_sub = (comp_a < comp_b);
 `endif
-assign {cy_sum, result_sum} = a + b;
-`ifdef OR1200_IMPL_ADDC
-assign {cy_csum, result_csum} = a + b + {`OR1200_OPERAND_WIDTH'd0, carry};
+`ifdef OR1200_IMPL_ADDC   
+assign carry_in = (alu_op==`OR1200_ALUOP_ADDC) ? 
+		  {{width-1{1'b0}},carry} : {width{1'b0}};
+`else
+assign carry_in = {width-1{1'b0}};
 `endif
+`ifdef OR1200_IMPL_SUB
+assign b_mux = (alu_op==`OR1200_ALUOP_SUB) ? (~b)+1 : b;
+`else
+assign b_mux = b;
+`endif   
+assign {cy_sum, result_sum} = (a + b_mux) + carry_in;
+// Numbers either both +ve and bit 31 of result set
+assign ov_sum = ((!a[width-1] & !b_mux[width-1]) & result_sum[width-1]) |
+// or both -ve and bit 31 of result clear
+		((a[width-1] & b_mux[width-1]) & !result_sum[width-1]);  
 assign result_and = a & b;
 
 //
@@ -145,9 +164,6 @@ end
 //
 always @(alu_op or alu_op2 or a or b or result_sum or result_and or macrc_op
 	 or shifted_rotated or mult_mac_result or flag or result_cust5 or carry
-`ifdef OR1200_IMPL_ADDC
-         or result_csum
-`endif
 `ifdef OR1200_IMPL_ALU_EXT
          or extended
 `endif	 
@@ -182,19 +198,15 @@ always @(alu_op or alu_op2 or a or b or result_sum or result_and or macrc_op
 		`OR1200_ALUOP_SHROT : begin 
 				result = shifted_rotated;
 		end
-		`OR1200_ALUOP_ADD : begin
-				result = result_sum;
-		end
 `ifdef OR1200_IMPL_ADDC
-		`OR1200_ALUOP_ADDC : begin
-				result = result_csum;
-		end
+		`OR1200_ALUOP_ADDC,
 `endif
 `ifdef OR1200_IMPL_SUB
-		`OR1200_ALUOP_SUB : begin
-				result = a - b;
+		`OR1200_ALUOP_SUB,
+`endif	 
+		`OR1200_ALUOP_ADD : begin
+				result = result_sum;
 		end
-`endif
 		`OR1200_ALUOP_XOR : begin
 				result = a ^ b;
 		end
@@ -245,22 +257,16 @@ end
 // Generate flag and flag write enable
 //
 always @(alu_op or result_sum or result_and or flagcomp
-`ifdef OR1200_IMPL_ADDC
-         or result_csum
-`endif
 ) begin
 	casez (alu_op)		// synopsys parallel_case
 `ifdef OR1200_ADDITIONAL_FLAG_MODIFIERS
+`ifdef OR1200_IMPL_ADDC
+		`OR1200_ALUOP_ADDC,
+`endif	 
 		`OR1200_ALUOP_ADD : begin
 			flagforw = (result_sum == 32'h0000_0000);
 			flag_we = 1'b1;
 		end
-`ifdef OR1200_IMPL_ADDC
-		`OR1200_ALUOP_ADDC : begin
-			flagforw = (result_csum == 32'h0000_0000);
-			flag_we = 1'b1;
-		end
-`endif
 		`OR1200_ALUOP_AND: begin
 			flagforw = (result_and == 32'h0000_0000);
 			flag_we = 1'b1;
@@ -282,9 +288,6 @@ end
 //
 always @(alu_op or cy_sum
 `ifdef OR1200_IMPL_CY
-`ifdef OR1200_IMPL_ADDC
-	or cy_csum
-`endif
 `ifdef OR1200_IMPL_SUB
 	or cy_sub
 `endif
@@ -292,16 +295,13 @@ always @(alu_op or cy_sum
 ) begin
 	casez (alu_op)		// synopsys parallel_case
 `ifdef OR1200_IMPL_CY
+`ifdef OR1200_IMPL_ADDC
+		`OR1200_ALUOP_ADDC,
+`endif	  
 		`OR1200_ALUOP_ADD : begin
 			cyforw = cy_sum;
 			cy_we = 1'b1;
 		end
-`ifdef OR1200_IMPL_ADDC
-		`OR1200_ALUOP_ADDC: begin
-			cyforw = cy_csum;
-			cy_we = 1'b1;
-		end
-`endif
 `ifdef OR1200_IMPL_SUB
 		`OR1200_ALUOP_SUB: begin
 			cyforw = cy_sub;
@@ -316,6 +316,31 @@ always @(alu_op or cy_sum
 	endcase
 end
 
+
+//
+// Generate SR[OV] write enable
+//
+always @(alu_op or ov_sum) begin
+	casez (alu_op)		// synopsys parallel_case
+`ifdef OR1200_IMPL_OV
+`ifdef OR1200_IMPL_ADDC
+		`OR1200_ALUOP_ADDC,
+`endif
+`ifdef OR1200_IMPL_SUB
+		`OR1200_ALUOP_SUB,
+`endif	 
+		`OR1200_ALUOP_ADD : begin
+			ovforw = ov_sum;
+			ov_we = 1'b1;
+		end
+`endif	  
+		default: begin
+			ovforw = 1'b0;
+			ov_we = 1'b0;
+		end
+	endcase
+end
+   
 //
 // Shifts and rotation
 //

programming_language: C
filename: or1200_cpu.v
change_type: ModificationType.MODIFY
diff: @@ -271,6 +271,10 @@ wire				flag_we_fpu;
 wire				carry;
 wire				cyforw;
 wire				cy_we_alu;
+wire				ovforw;
+wire				ov_we_alu;
+wire				ovforw_mult_mac;
+wire				ov_we_mult_mac;   
 wire				cy_we_rf;
 wire				lsu_stall;
 wire				epcr_we;
@@ -292,6 +296,7 @@ wire    			fpu_except_started;
 wire	[31:0]			wb_insn;
 wire				sig_syscall;
 wire				sig_trap;
+wire    			sig_range;
 wire				sig_fp;
 wire	[31:0]			spr_dat_cfgr;
 wire	[31:0]			spr_dat_rf;
@@ -393,9 +398,15 @@ assign flagforw = (flag_we_alu & flagforw_alu) | (flagforw_fpu & flag_we_fpu);
 assign flag_we = (flag_we_alu | flag_we_fpu) & ~abort_mvspr;
 
 //
-//  Flag for any MTSPR instructions, that must block execution, to indicate done
+// Flag for any MTSPR instructions, that must block execution, to indicate done
 //
 assign mtspr_done = mtspr_dc_done;
+
+//
+// Range exception
+//
+assign sig_range = sr[`OR1200_SR_OV];
+   
    
    
 //
@@ -591,6 +602,8 @@ or1200_alu or1200_alu(
 	.flag_we(flag_we_alu),
 	.cyforw(cyforw),
 	.cy_we(cy_we_alu),
+	.ovforw(ovforw),
+	.ov_we(ov_we_alu),		      
 	.flag(flag),
 	.carry(carry)
 );
@@ -641,6 +654,8 @@ or1200_mult_mac or1200_mult_mac(
 	.mac_op(mac_op),
 	.alu_op(alu_op),
 	.result(mult_mac_result),
+	.ovforw(ovforw_mult_mac), 
+	.ov_we(ov_we_mult_mac),
 	.mult_mac_stall(mult_mac_stall),
 	.spr_cs(spr_cs[`OR1200_SPR_GROUP_MAC]),
 	.spr_write(spr_we),
@@ -666,6 +681,8 @@ or1200_sprs or1200_sprs(
 	.cyforw(cyforw),
 	.cy_we(cy_we_rf),
 	.carry(carry),
+	.ovforw(ovforw | ovforw_mult_mac),
+	.ov_we(ov_we_alu | ov_we_mult_mac),
 	.to_wbmux(sprs_dataout),
 
 	.du_addr(du_addr),
@@ -803,7 +820,7 @@ or1200_except or1200_except(
 	.sig_dbuserr(except_dbuserr),
 	.sig_illegal(except_illegal),
 	.sig_align(except_align),
-	.sig_range(1'b0),
+	.sig_range(sig_range),
 	.sig_dtlbmiss(except_dtlbmiss),
 	.sig_dmmufault(except_dmmufault),
 	.sig_int(sig_int),

programming_language: Rust
filename: or1200_defines.v
change_type: ModificationType.MODIFY
diff: @@ -299,7 +299,7 @@
 // If you don't use them, then disable implementation
 // to save area.
 //
-//`define OR1200_IMPL_ADDC
+`define OR1200_IMPL_ADDC
 
 //
 // Implement l.sub instruction
@@ -321,7 +321,24 @@
 // instructions and if these three insns are not
 // implemented there is not much point having SR[CY].
 //
-//`define OR1200_IMPL_CY
+`define OR1200_IMPL_CY
+
+//
+// Implement carry bit SR[OV]
+//
+// Compiler doesn't use this, but other code may like
+// to.
+//
+`define OR1200_IMPL_OV
+
+//
+// Implement carry bit SR[OVE]
+//
+// Overflow interrupt indicator. When enabled, SR[OV] flag
+// does not remain asserted after exception.
+//
+`define OR1200_IMPL_OVE
+
 
 //
 // Implement rotate in the ALU
@@ -825,9 +842,9 @@
 `define OR1200_SR_LEE  7
 `define OR1200_SR_CE   8
 `define OR1200_SR_F    9
-`define OR1200_SR_CY   10	// Unused
-`define OR1200_SR_OV   11	// Unused
-`define OR1200_SR_OVE  12	// Unused
+`define OR1200_SR_CY   10	// Optional
+`define OR1200_SR_OV   11	// Optional
+`define OR1200_SR_OVE  12	// Optional
 `define OR1200_SR_DSX  13	// Unused
 `define OR1200_SR_EPH  14
 `define OR1200_SR_FO   15

programming_language: Objective-C
filename: or1200_except.v
change_type: ModificationType.MODIFY
diff: @@ -176,7 +176,8 @@ reg	[2:0]			delayed_tee;
 wire				int_pending;
 wire				tick_pending;
 wire    			fp_pending;
-   
+wire    			range_pending;
+			
 reg trace_trap      ;
 reg ex_freeze_prev;
 reg sr_ted_prev;
@@ -207,6 +208,13 @@ assign tick_pending = sig_tick & (sr[`OR1200_SR_TEE] |
 
 assign fp_pending = sig_fp & fpcsr_fpee & ~ex_freeze & ~ex_branch_taken 
 		    & ~ex_dslot;
+
+`ifdef OR1200_IMPL_OVE   
+assign range_pending =  sig_range & sr[`OR1200_SR_OVE] & ~ex_freeze & 
+		       ~ex_branch_taken & ~ex_dslot;
+`else
+assign range_pending = 0;
+`endif   
    
 // Abort write into RF by load & other instructions   
 assign abort_ex = sig_dbuserr | sig_dmmufault | sig_dtlbmiss | sig_align | 
@@ -234,7 +242,7 @@ assign except_trig = {
 		      sig_syscall       & ~du_dsr[`OR1200_DU_DSR_SCE] & ~ex_freeze,
 		      sig_dmmufault	& ~du_dsr[`OR1200_DU_DSR_DPFE],
 		      sig_dbuserr	& ~du_dsr[`OR1200_DU_DSR_BUSEE],
-		      sig_range		& ~du_dsr[`OR1200_DU_DSR_RE],
+		      range_pending	& ~du_dsr[`OR1200_DU_DSR_RE],
 		      fp_pending	& ~du_dsr[`OR1200_DU_DSR_FPE],
 		      int_pending 	& ~du_dsr[`OR1200_DU_DSR_IE],
 		      tick_pending	& ~du_dsr[`OR1200_DU_DSR_TTE]
@@ -260,7 +268,7 @@ assign except_stop = {
 			sig_dtlbmiss		& du_dsr[`OR1200_DU_DSR_DME],
 			sig_dmmufault		& du_dsr[`OR1200_DU_DSR_DPFE],
 			sig_dbuserr		& du_dsr[`OR1200_DU_DSR_BUSEE],
-			sig_range		& du_dsr[`OR1200_DU_DSR_RE],
+			range_pending		& du_dsr[`OR1200_DU_DSR_RE],
 			sig_trap		& du_dsr[`OR1200_DU_DSR_TE],
 		        fp_pending  		& du_dsr[`OR1200_DU_DSR_FPE],
 			sig_syscall		& du_dsr[`OR1200_DU_DSR_SCE] & ~ex_freeze

programming_language: C
filename: or1200_mult_mac.v
change_type: ModificationType.MODIFY
diff: @@ -65,6 +65,9 @@ module or1200_mult_mac(
 		       ex_freeze, id_macrc_op, macrc_op, a, b, mac_op, alu_op, 
 		       result, mult_mac_stall,
 
+		       // Overflow
+		       ovforw, ov_we,
+		       
 		       // SPR interface
 		       spr_cs, spr_write, spr_addr, spr_dat_i, spr_dat_o
 		       );
@@ -93,7 +96,8 @@ module or1200_mult_mac(
    input [`OR1200_ALUOP_WIDTH-1:0] 	alu_op;
    output [width-1:0] 			result;
    output				mult_mac_stall;
-
+   output 				ovforw, ov_we;
+   
    //
    // SPR interface
    //
@@ -154,9 +158,11 @@ module or1200_mult_mac(
  `else
    reg [width-1:0] 			div_quot_r;      
    reg [width-1:0] 			div_quot_generic;   
- `endif   
+ `endif
+   wire 				div_by_zero;
 `endif
-
+   reg 					ovforw, ov_we;
+   
    //
    // Combinatorial logic
    //
@@ -189,6 +195,9 @@ module or1200_mult_mac(
    assign y = (alu_op_sdiv | alu_op_smul) & b[31] ? ~b + 32'b1 : 
 	      alu_op_div | alu_op_mul | (|mac_op) ? b : 32'd0;
 
+   assign div_by_zero = !(|b) & alu_op_div;
+   
+
    // Used to indicate when we should check for new multiply or MAC ops
    always @(posedge clk or `OR1200_RST_EVENT rst)
      if (rst == `OR1200_RST_VALUE)
@@ -228,7 +237,43 @@ module or1200_mult_mac(
 `else
        result = {width{1'b0}};    
 `endif    
-     endcase
+     endcase // casez (alu_op)
+
+
+   //
+   // Overflow generation
+   //
+   always @*
+     casez(alu_op)	// synopsys parallel_case
+`ifdef OR1200_IMPL_OV       
+ `ifdef OR1200_MULT_IMPLEMENTED
+       `OR1200_ALUOP_MUL: begin
+	  // Actually doing unsigned multiply internally, and then negate on
+	  // output as appropriate, so if sign bit is set, then is overflow
+	  ovforw = mul_prod_r[31];
+	  ov_we = 1;
+       end
+       `OR1200_ALUOP_MULU : begin
+	  // Overflow on unsigned multiply is simpler.
+	  ovforw = mul_prod_r[32];
+	  ov_we = 1;
+       end
+ `endif //  `ifdef OR1200_MULT_IMPLEMENTED
+ `ifdef OR1200_DIV_IMPLEMENTED
+       `OR1200_ALUOP_DIVU,
+       `OR1200_ALUOP_DIV: begin
+	  // Overflow on divide by zero
+	  ovforw = div_by_zero;
+	  ov_we = 1;
+       end
+ `endif
+`endif //  `ifdef OR1200_IMPL_OV
+       default: begin
+	  ovforw = 0;
+	  ov_we = 0;
+       end
+     endcase // casez (alu_op)
+   
 
 `ifdef OR1200_MULT_IMPLEMENTED
  `ifdef OR1200_MULT_SERIAL
@@ -398,6 +443,11 @@ module or1200_mult_mac(
 	div_free <=  1'b1;
 	div_cntr <=  6'b00_0000;
      end
+     else if (div_by_zero) begin
+	div_quot_r <=  64'h0000_0000_0000_0000;
+	div_free <=  1'b1;
+	div_cntr <=  6'b00_0000;
+     end
      else if (|div_cntr) begin
 	if (div_tmp[31])
 	  div_quot_r <=  {div_quot_r[62:0], 1'b0};

programming_language: C
filename: or1200_sprs.v
change_type: ModificationType.MODIFY
diff: @@ -57,6 +57,7 @@ module or1200_sprs(
 
 		   // Internal CPU interface
 		   flagforw, flag_we, flag, cyforw, cy_we, carry,
+		   ovforw, ov_we,
 		   addrbase, addrofs, dat_i, branch_op, ex_spr_read, 
 		   ex_spr_write, 
 		   epcr, eear, esr, except_started,
@@ -96,6 +97,8 @@ module or1200_sprs(
    input 				cyforw;		// From ALU
    input 				cy_we;		// From ALU
    output 				carry;		// SR[CY]
+   input 				ovforw;		// From ALU
+   input 				ov_we;		// From ALU
    input [width-1:0] 			addrbase;	// SPR base address
    input [15:0] 			addrofs;	// SPR offset
    input [width-1:0] 			dat_i;		// SPR write data
@@ -285,18 +288,23 @@ module or1200_sprs(
    //
    // What to write into SR
    //
-   assign to_sr[`OR1200_SR_FO:`OR1200_SR_OV] 
-	    = (except_started) ? sr[`OR1200_SR_FO:`OR1200_SR_OV] :
+   assign to_sr[`OR1200_SR_FO:`OR1200_SR_OVE] 
+	    = (except_started) ? {sr[`OR1200_SR_FO:`OR1200_SR_DSX],1'b0} :
 	      (branch_op == `OR1200_BRANCHOP_RFE) ? 
-	      esr[`OR1200_SR_FO:`OR1200_SR_OV] : (spr_we && sr_sel) ? 
-	      {1'b1, spr_dat_o[`OR1200_SR_FO-1:`OR1200_SR_OV]} :
-	      sr[`OR1200_SR_FO:`OR1200_SR_OV];
+	      esr[`OR1200_SR_FO:`OR1200_SR_OVE] : (spr_we && sr_sel) ? 
+	      {1'b1, spr_dat_o[`OR1200_SR_FO-1:`OR1200_SR_OVE]} :
+	      sr[`OR1200_SR_FO:`OR1200_SR_OVE];
    assign to_sr[`OR1200_SR_TED] 
 	    = (except_started) ? 1'b1 :
 	      (branch_op == `OR1200_BRANCHOP_RFE) ? esr[`OR1200_SR_TED] :
 	      (spr_we && sr_sel) ? spr_dat_o[`OR1200_SR_TED] :
 	      sr[`OR1200_SR_TED];
-   
+   assign to_sr[`OR1200_SR_OV] 
+	    = (except_started) ? sr[`OR1200_SR_OV] :
+	      (branch_op == `OR1200_BRANCHOP_RFE) ? esr[`OR1200_SR_OV] :
+	      ov_we ? ovforw :
+	      (spr_we && sr_sel) ? spr_dat_o[`OR1200_SR_OV] :
+	      sr[`OR1200_SR_OV];
    assign to_sr[`OR1200_SR_CY] 
 	    = (except_started) ? sr[`OR1200_SR_CY] :
 	      (branch_op == `OR1200_BRANCHOP_RFE) ? esr[`OR1200_SR_CY] :
@@ -345,7 +353,7 @@ module or1200_sprs(
    // Write enables for system SPRs
    //
    assign sr_we = (spr_we && sr_sel) | (branch_op == `OR1200_BRANCHOP_RFE) | 
-		  flag_we | cy_we;
+		  flag_we | cy_we | ov_we;
    assign pc_we = (du_write && (npc_sel | ppc_sel));
    assign epcr_we = (spr_we && epcr_sel);
    assign eear_we = (spr_we && eear_sel);
@@ -375,7 +383,7 @@ module or1200_sprs(
    // Carry alias
    //
    assign carry = sr[`OR1200_SR_CY];
-
+   
    //
    // Supervision register
    //

programming_language: C++