kronos icon indicating copy to clipboard operation
kronos copied to clipboard

Execution time depends on a data value passed to an addi instruction

Open KatCe opened this issue 1 year ago • 0 comments

I have found a security vulnerability where the execution time is influenced by the data operand of an 'addi' instruction. The following test bench reproduces the problem. When setting the local parameter 'fast' of the test bench to 1, value 0 is loaded into t6. This could be any value, except for 0xFFFFFFFF. When the parameter is set 0, 0xFFFFFFFF is loaded into t6, which influences the timing of the program and therefore leaks the value of t6.


module tb_top;

logic clk, rstz;

logic [31:0] instr_addr;
logic [31:0] instr_data;
logic instr_req;
logic instr_ack;
logic [31:0] data_addr;
logic [31:0] data_rd_data;
logic [31:0] data_wr_data;
logic [3:0] data_mask;
logic data_wr_en;
logic data_req;
logic data_ack;
// ============================================================
// Kronos
// ============================================================

kronos_core #(
  .BOOT_ADDR(32'h0),
  .FAST_BRANCH(1),
  .EN_COUNTERS(1),
  .EN_COUNTERS64B(1),
  .CATCH_ILLEGAL_INSTR(1),
  .CATCH_MISALIGNED_JMP(0),
  .CATCH_MISALIGNED_LDST(0)
) u_core (
  .clk               (clk         ),
  .rstz              (rstz        ),
  .instr_addr        (instr_addr  ),
  .instr_data        (instr_data  ),
  .instr_req         (  ),
  .instr_ack         (1'b1   ),
  .data_addr         (data_addr   ),
  .data_rd_data      (data_rd_data),
  .data_wr_data      (),
  .data_mask         (data_mask   ),
  .data_wr_en        (data_wr_en  ),
  .data_req          (   ),
  .data_ack          (1'b1    ),
  .software_interrupt(1'b0        ),
  .timer_interrupt   (1'b0        ),
  .external_interrupt(1'b0        )
);

initial begin
  clk = 0;
  rstz = 0;

  fork
    forever #1ns clk = ~clk;
  join_none

  data_rd_data = 32'h00080AA8;

end

default clocking cb @(posedge clk); endclocking

// !!! Change this parameter to run either the fast or the slow test
localparam logic fast = 0;

always @(posedge clk) begin

  ##4 rstz = 1;

  // Start with one cycle delay, because Kronos does not take the instruction in the first cycle after reset
  ##1

  for (int i = 0; i < 1; i++) begin
    instr_data <= 32'h00000013;
    ##1;
  end

  // Load some data from memory 
  // lw      t6, 16(t4) will load data_rd_data into t6
  instr_data <= 32'h10eaf83;
  if (fast == 1)
    data_rd_data <= 32'b0;
  else
    data_rd_data <= '1; // all ones
  ##1

  // some NOPs
  for (int i = 0; i < 1; i++) begin
    instr_data <= 32'h00000013;
    ##1;
  end

  //csrw	mtval,gp     // an unrelated csr write
  instr_data <= 32'h34319073;
  ##1

  // NOP
  instr_data <= 32'h00000013;
  ##1

  //addi    t0, t6, -1278 -----> the malicious addi
  instr_data <= 32'hb02f8293;
  ##1


  // csrw	mtval,gp     // an unrelated csr write
  instr_data <= 32'h34319073;
  ##1

  //c.jr    s5
  instr_data <= 32'h34038a82;
  // data_rd_data <= 32'h0;
  ##1

  //beq     s5, s5, pc + 2048
  instr_data <= 32'h15a80e3;
  ##1

  // //
  instr_data <= 32'h4160078;
  ##1;

    // end with some random instructions 
  for (int i = 0; i < 10; i++) begin
    // NOP
    instr_data <= 32'h00000013;
    ##1;

    // lui	t1,0x10
    instr_data <= 32'h00010337;
    ##1;

    // addi	t1,t1,1
    instr_data <= 32'h00130313;
    ##1;

    // addi	t1,t1,1
    instr_data <= 32'h00130313;
    ##1;

    // addi	t1,t1,1
    instr_data <= 32'h00130313;
    ##1;

  end
end

endmodule

Screenshot from 2023-12-21 19-02-30 Screenshot from 2023-12-21 19-02-21

I observed a similar scenario with the 'slti(u)' instruction.

Note that data_ack had to be constant high. While this is only valid in the Wishbone classic mode, and Kronos supports pipelined mode, a CPU should not rely on valid external inputs. Connecting a memory operating in classic mode or fault injections could trigger the vulnerability.

KatCe avatar Dec 21 '23 18:12 KatCe