CVE-2021-40507 (Other, published 2023-04-18T12:15Z)
Description: value: 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.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: CWE-287 \u2014 Improper Authentication
When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
Repository: https://github.com/openrisc/or1200
Files changed:
rtl/verilog/or1200_alu.v
rtl/verilog/or1200_cpu.v
rtl/verilog/or1200_defines.v
rtl/verilog/or1200_except.v
rtl/verilog/or1200_mult_mac.v
rtl/verilog/or1200_sprs.v
Commit message: or1200: add carry, overflow bits, and range exception


Vulnerable code:
`ifdef OR1200_IMPL_ADDC
wire	[width-1:0]		result_csum;
wire				cy_csum;
`endif
reg				cyforw;
assign cy_sub = a < b;
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
         or result_csum
`endif
		`OR1200_ALUOP_ADD : begin
				result = result_sum;
		end
		`OR1200_ALUOP_ADDC : begin
				result = result_csum;
		end
		`OR1200_ALUOP_SUB : begin
				result = a - b;
`endif
`ifdef OR1200_IMPL_ADDC
         or result_csum
`endif
`ifdef OR1200_IMPL_ADDC
		`OR1200_ALUOP_ADDC : begin
			flagforw = (result_csum == 32'h0000_0000);
			flag_we = 1'b1;
		end
`endif
`ifdef OR1200_IMPL_ADDC
	or cy_csum
`endif
`ifdef OR1200_IMPL_ADDC
		`OR1200_ALUOP_ADDC: begin
			cyforw = cy_csum;
			cy_we = 1'b1;
		end
`endif
//  Flag for any MTSPR instructions, that must block execution, to indicate done
	.sig_range(1'b0),
//`define OR1200_IMPL_ADDC
//`define OR1200_IMPL_CY
`define OR1200_SR_CY   10	// Unused
`define OR1200_SR_OV   11	// Unused
`define OR1200_SR_OVE  12	// Unused
   
		      sig_range		& ~du_dsr[`OR1200_DU_DSR_RE],
			sig_range		& du_dsr[`OR1200_DU_DSR_RE],

 `endif   

     endcase
   assign to_sr[`OR1200_SR_FO:`OR1200_SR_OV] 
	    = (except_started) ? sr[`OR1200_SR_FO:`OR1200_SR_OV] :
	      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];
   
		  flag_we | cy_we;


Fixed code:
	ovforw, ov_we,
output				ovforw;
output				ov_we;
reg				cyforw;
reg				ovforw;
reg				ov_we;   
wire    			ov_sum;
wire    [width-1:0] 		carry_in;

wire    [width-1:0]		b_mux;
   
   
assign cy_sub = (comp_a < comp_b);
`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}};
`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]);  
		`OR1200_ALUOP_ADDC,
		`OR1200_ALUOP_SUB,
`endif	 
		`OR1200_ALUOP_ADD : begin
				result = result_sum;
`ifdef OR1200_IMPL_ADDC
		`OR1200_ALUOP_ADDC,
`endif	 
`ifdef OR1200_IMPL_ADDC
		`OR1200_ALUOP_ADDC,
`endif	  

//
// 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
   
wire				ovforw;
wire				ov_we_alu;
wire				ovforw_mult_mac;
wire				ov_we_mult_mac;   
wire    			sig_range;
// Flag for any MTSPR instructions, that must block execution, to indicate done

//
// Range exception
//
assign sig_range = sr[`OR1200_SR_OV];
   
	.ovforw(ovforw),
	.ov_we(ov_we_alu),		      
	.ovforw(ovforw_mult_mac), 
	.ov_we(ov_we_mult_mac),
	.ovforw(ovforw | ovforw_mult_mac),
	.ov_we(ov_we_alu | ov_we_mult_mac),
	.sig_range(sig_range),
`define OR1200_IMPL_ADDC
`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

`define OR1200_SR_CY   10	// Optional
`define OR1200_SR_OV   11	// Optional
`define OR1200_SR_OVE  12	// Optional
wire    			range_pending;
			

`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   
		      range_pending	& ~du_dsr[`OR1200_DU_DSR_RE],
			range_pending		& du_dsr[`OR1200_DU_DSR_RE],
		       // Overflow
		       ovforw, ov_we,
		       
   output 				ovforw, ov_we;
   
 `endif
   wire 				div_by_zero;
   reg 					ovforw, ov_we;
   
   assign div_by_zero = !(|b) & alu_op_div;
   

     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)
   
     else if (div_by_zero) begin
	div_quot_r <=  64'h0000_0000_0000_0000;
	div_free <=  1'b1;
	div_cntr <=  6'b00_0000;
     end
		   ovforw, ov_we,
   input 				ovforw;		// From ALU
   input 				ov_we;		// From ALU
   assign to_sr[`OR1200_SR_FO:`OR1200_SR_OVE] 
	    = (except_started) ? {sr[`OR1200_SR_FO:`OR1200_SR_DSX],1'b0} :
	      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_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];
		  flag_we | cy_we | ov_we;