Unauthorized Error Injection Can Degrade Hardware Redundancy
Description
Unauthorized Error Injection Can Degrade Hardware Redundancy occurs when an unauthorized agent can inject errors into a redundant block to deprive the system of redundancy or put the system in a degraded operating mode. Hardware designers implement redundant blocks to maintain performance and reliability when primary components fail. However, if the design permits unauthorized parties to inject errors into these redundant systems, they can disable backup pathways, forcing the system into degraded operational states and potentially enabling subsequent attacks.
Risk
Unauthorized error injection has severe implications. Redundancy eliminated enabling single-point failures. Safety margins removed. Fault tolerance defeated. System reliability degraded. Subsequent attacks enabled. Denial of service through induced failures. Quality degradation. Performance impact. Resource consumption increase. High likelihood when error injection interfaces lack access control.
Solution
Prevent error injection during normal runtime during architecture and design phase. Implement access controls on error-injection interfaces. Disallow error injection in standard operation modes during implementation phase. Add access controls to error-injection mechanisms. Layer access controls over unprotected interfaces during integration phase.
Common Consequences
| Impact | Details |
|---|---|
| Availability | Scope: Availability Denial of service through system crashes, restarts, and instability. |
| Integrity | Scope: Integrity Quality degradation and reduced performance through disabled redundancy. |
Example Code
Vulnerable Code
// Vulnerable: Redundant system without error injection protection
module vulnerable_redundant_system (
input wire clk,
input wire rst_n,
// Primary and redundant computation paths
input wire [31:0] data_in,
input wire compute_start,
// Error injection interface (VULNERABLE: no protection)
input wire inject_error_primary,
input wire inject_error_redundant,
input wire [7:0] error_pattern,
output reg [31:0] result,
output reg result_valid,
output reg redundancy_mismatch
);
reg [31:0] primary_result;
reg [31:0] redundant_result;
// Primary computation path
always @(posedge clk) begin
if (compute_start) begin
primary_result <= compute_function(data_in);
// VULNERABLE: Error can be injected without authorization
if (inject_error_primary) begin
primary_result <= primary_result ^ {24'b0, error_pattern};
end
end
end
// Redundant computation path
always @(posedge clk) begin
if (compute_start) begin
redundant_result <= compute_function(data_in);
// VULNERABLE: Error injection to redundant path
if (inject_error_redundant) begin
redundant_result <= redundant_result ^ {24'b0, error_pattern};
end
end
end
// Comparison logic
always @(posedge clk) begin
redundancy_mismatch <= (primary_result != redundant_result);
if (!redundancy_mismatch) begin
result <= primary_result;
result_valid <= 1'b1;
end else begin
// System enters degraded mode
result_valid <= 1'b0;
end
end
// Attack:
// 1. Inject continuous errors to redundant path
// 2. System sees constant mismatch
// 3. Redundancy appears failed
// 4. System operates in degraded mode
// 5. Single fault now causes complete failure
endmodule
// Vulnerable: ECC memory without error injection protection
module vulnerable_ecc_memory (
input wire clk,
input wire rst_n,
input wire [15:0] addr,
input wire [31:0] data_in,
input wire write_en,
input wire read_en,
// VULNERABLE: Error injection for testing
input wire inject_single_bit_error,
input wire inject_multi_bit_error,
input wire [4:0] error_bit_position,
output reg [31:0] data_out,
output reg single_bit_corrected,
output reg multi_bit_detected
);
reg [38:0] memory [0:65535]; // 32 data + 7 ECC bits
always @(posedge clk) begin
if (write_en) begin
memory[addr] <= {compute_ecc(data_in), data_in};
end
if (read_en) begin
reg [38:0] raw_data = memory[addr];
// VULNERABLE: Error injection without access control
if (inject_single_bit_error) begin
raw_data[error_bit_position] <= ~raw_data[error_bit_position];
end
if (inject_multi_bit_error) begin
raw_data[error_bit_position] <= ~raw_data[error_bit_position];
raw_data[error_bit_position + 1] <= ~raw_data[error_bit_position + 1];
end
// ECC decode
{single_bit_corrected, multi_bit_detected, data_out} <= ecc_decode(raw_data);
end
end
// Attack: Continuously inject multi-bit errors
// System logs failures, may disable ECC protection
// Memory now vulnerable to actual errors
endmodule
Fixed Code
// Fixed: Redundant system with protected error injection
module secure_redundant_system (
input wire clk,
input wire rst_n,
// Primary and redundant computation paths
input wire [31:0] data_in,
input wire compute_start,
// Error injection interface (FIXED: protected)
input wire inject_error_primary,
input wire inject_error_redundant,
input wire [7:0] error_pattern,
input wire test_mode_enabled, // FIXED: Lifecycle signal
input wire privileged_access, // FIXED: Access control
input wire boot_complete, // FIXED: Runtime indicator
output reg [31:0] result,
output reg result_valid,
output reg redundancy_mismatch,
output reg injection_denied
);
reg [31:0] primary_result;
reg [31:0] redundant_result;
// FIXED: Error injection control
wire error_injection_allowed = test_mode_enabled &&
privileged_access &&
!boot_complete;
// Primary computation path
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
primary_result <= 32'b0;
injection_denied <= 1'b0;
end else if (compute_start) begin
primary_result <= compute_function(data_in);
injection_denied <= 1'b0;
// FIXED: Error injection only in allowed modes
if (inject_error_primary) begin
if (error_injection_allowed) begin
primary_result <= primary_result ^ {24'b0, error_pattern};
end else begin
// FIXED: Deny unauthorized injection
injection_denied <= 1'b1;
end
end
end
end
// Redundant computation path (similar protection)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
redundant_result <= 32'b0;
end else if (compute_start) begin
redundant_result <= compute_function(data_in);
if (inject_error_redundant && error_injection_allowed) begin
redundant_result <= redundant_result ^ {24'b0, error_pattern};
end
end
end
// FIXED: Comparison with injection attempt logging
always @(posedge clk) begin
redundancy_mismatch <= (primary_result != redundant_result);
if (!redundancy_mismatch) begin
result <= primary_result;
result_valid <= 1'b1;
end else begin
result_valid <= 1'b0;
end
end
// FIXED: Lock out test mode after boot
always @(posedge boot_complete) begin
// Test mode automatically disabled after boot
// Error injection no longer possible
end
endmodule
// Fixed: ECC memory with protected error injection
module secure_ecc_memory (
input wire clk,
input wire rst_n,
input wire [15:0] addr,
input wire [31:0] data_in,
input wire write_en,
input wire read_en,
// FIXED: Protected error injection
input wire inject_single_bit_error,
input wire inject_multi_bit_error,
input wire [4:0] error_bit_position,
input wire test_mode,
input wire secure_debug_enabled, // FIXED: Debug authentication
input wire manufacturing_mode, // FIXED: Lifecycle
output reg [31:0] data_out,
output reg single_bit_corrected,
output reg multi_bit_detected,
output reg injection_blocked
);
reg [38:0] memory [0:65535];
// FIXED: Error injection only during manufacturing test
wire injection_allowed = manufacturing_mode &&
(test_mode || secure_debug_enabled);
// FIXED: Rate limiting for error injection
reg [7:0] injection_count;
localparam MAX_INJECTIONS = 100;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
injection_count <= 8'b0;
injection_blocked <= 1'b0;
end else begin
injection_blocked <= 1'b0;
if (write_en) begin
memory[addr] <= {compute_ecc(data_in), data_in};
end
if (read_en) begin
reg [38:0] raw_data;
raw_data = memory[addr];
// FIXED: Protected error injection
if (inject_single_bit_error || inject_multi_bit_error) begin
if (injection_allowed && injection_count < MAX_INJECTIONS) begin
if (inject_single_bit_error) begin
raw_data[error_bit_position] = ~raw_data[error_bit_position];
end
if (inject_multi_bit_error) begin
raw_data[error_bit_position] = ~raw_data[error_bit_position];
raw_data[error_bit_position + 1] = ~raw_data[error_bit_position + 1];
end
injection_count <= injection_count + 1;
end else begin
// FIXED: Block unauthorized or excessive injection
injection_blocked <= 1'b1;
end
end
// ECC decode
{single_bit_corrected, multi_bit_detected, data_out} <= ecc_decode(raw_data);
end
end
end
// FIXED: Lock injection capability in production
always @(posedge clk) begin
if (!manufacturing_mode) begin
injection_count <= MAX_INJECTIONS; // Effectively disable
end
end
endmodule
// Fixed: Redundancy manager with attack detection
module secure_redundancy_manager (
input wire clk,
input wire rst_n,
// Redundant inputs
input wire [31:0] input_a,
input wire [31:0] input_b,
input wire [31:0] input_c, // Triple modular redundancy
input wire inputs_valid,
// Status
output reg [31:0] voted_output,
output reg output_valid,
output reg single_failure,
output reg double_failure,
output reg attack_detected
);
// FIXED: Track failure patterns to detect attacks
reg [15:0] failure_history;
reg [7:0] failure_count_a;
reg [7:0] failure_count_b;
reg [7:0] failure_count_c;
localparam ATTACK_THRESHOLD = 10; // Failures in window
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
failure_history <= 16'b0;
failure_count_a <= 8'b0;
failure_count_b <= 8'b0;
failure_count_c <= 8'b0;
attack_detected <= 1'b0;
end else if (inputs_valid) begin
// Triple modular redundancy voting
reg match_ab = (input_a == input_b);
reg match_bc = (input_b == input_c);
reg match_ac = (input_a == input_c);
// FIXED: Track which channels fail
if (!match_ab && !match_ac) failure_count_a <= failure_count_a + 1;
if (!match_ab && !match_bc) failure_count_b <= failure_count_b + 1;
if (!match_ac && !match_bc) failure_count_c <= failure_count_c + 1;
// FIXED: Detect attack pattern (consistent failures on one channel)
if (failure_count_a > ATTACK_THRESHOLD ||
failure_count_b > ATTACK_THRESHOLD ||
failure_count_c > ATTACK_THRESHOLD) begin
attack_detected <= 1'b1;
end
// Voting logic
if (match_ab || match_ac) begin
voted_output <= input_a;
output_valid <= 1'b1;
single_failure <= !(match_ab && match_ac && match_bc);
double_failure <= 1'b0;
end else if (match_bc) begin
voted_output <= input_b;
output_valid <= 1'b1;
single_failure <= 1'b1;
double_failure <= 1'b0;
end else begin
// All three disagree
output_valid <= 1'b0;
double_failure <= 1'b1;
end
end
end
// FIXED: Periodic reset of failure counters (sliding window)
reg [15:0] window_counter;
localparam WINDOW_SIZE = 10000;
always @(posedge clk) begin
window_counter <= window_counter + 1;
if (window_counter >= WINDOW_SIZE) begin
window_counter <= 16'b0;
failure_count_a <= failure_count_a >> 1; // Decay
failure_count_b <= failure_count_b >> 1;
failure_count_c <= failure_count_c >> 1;
end
end
endmodule
CVE Examples
- CVE-2019-11135: TSX Asynchronous Abort (TAA) allowed error injection affecting CPU redundancy mechanisms.
- CVE-2020-0549: L1D Eviction Sampling vulnerability exploitable through error injection.
Related CWEs
- CWE-284: Improper Access Control (parent)
- CWE-1198: Privilege Separation and Access Control Issues (category)
- CAPEC-624: Hardware Fault Injection
- CAPEC-625: Mobile Device Fault Injection
References
- MITRE Corporation. "CWE-1334: Unauthorized Error Injection Can Degrade Hardware Redundancy." https://cwe.mitre.org/data/definitions/1334.html
- IEEE. "Fault Tolerance in Digital Systems"
- NASA. "Fault Tolerant Design Handbook"