Missing Ability to Patch ROM Code
Description
Missing Ability to Patch ROM Code occurs when the absence of patching capability for ROM code leaves systems vulnerable to unfixable security issues discovered post-deployment. Systems using Root-of-Trust (RoT) mechanisms execute immutable ROM code during boot. When vulnerabilities are discovered after shipping, the inability to patch ROM creates a permanent security risk—any security vulnerabilities discovered in the ROM code can never be fixed for the systems that are already in use. This is particularly critical for secure boot implementations and cryptographic operations performed in ROM.
Risk
Missing ROM patching has severe implications. Permanent security vulnerabilities. Unfixable bugs in deployed systems. Root of trust compromised permanently. Cryptographic flaws unrepairable. Secure boot bypass possible. Product recalls may be necessary. Reduced system maintainability. High likelihood of permanent compromise.
Solution
Implement secure patch support allowing ROM code patching on subsequent boots during architecture, design, and implementation phases. Note that hardware initialization and signature verification may remain non-patchable. Support field-programmable patches using hardware fuses for limited post-deployment patching. Include patch loading in early boot sequence. Use signed patches to prevent unauthorized modifications.
Common Consequences
| Impact | Details |
|---|---|
| Maintainability | Scope: Other Systems unable to be patched remain in vulnerable states indefinitely, reducing maintainability. |
Example Code
Vulnerable Code
// Vulnerable: ROM without patching capability
module vulnerable_boot_rom (
input wire clk,
input wire rst_n,
input wire [11:0] rom_addr,
input wire ariane_boot_sel_i, // Boot selection signal
output wire [31:0] rom_rdata
);
// Fixed ROM content - cannot be changed after fabrication
reg [31:0] rom_linux [0:4095];
reg [31:0] rom_rdata_linux;
// ROM initialization (synthesized into mask ROM or OTP)
initial begin
$readmemh("linux_bootrom.hex", rom_linux);
end
always @(posedge clk) begin
rom_rdata_linux <= rom_linux[rom_addr];
end
// VULNERABLE: Bug - always outputs linux ROM regardless of selection
// This bug cannot be fixed once the chip is fabricated!
assign rom_rdata = (ariane_boot_sel_i) ? rom_rdata_linux : rom_rdata_linux;
// If a security vulnerability is found in rom_linux:
// - Cannot update the ROM content
// - Cannot fix the selection logic bug
// - All deployed devices remain vulnerable
endmodule
// Vulnerable: Secure boot with unpatchable verification
module vulnerable_secure_boot_rom (
input wire clk,
input wire rst_n,
input wire [31:0] firmware_addr,
input wire [31:0] firmware_data,
input wire [2047:0] firmware_signature,
output reg boot_authorized,
output reg boot_error
);
// VULNERABLE: Public key embedded in ROM
// Cannot be changed if key is compromised
reg [2047:0] rom_public_key;
initial begin
// Key is permanently embedded
rom_public_key = 2048'h<fixed_public_key>;
end
// VULNERABLE: Signature verification logic in ROM
// Any bugs here are permanent
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
boot_authorized <= 1'b0;
boot_error <= 1'b0;
end else begin
// VULNERABLE: If RSA verification has a bug (e.g., padding oracle)
// it cannot be fixed
if (rsa_verify(firmware_data, firmware_signature, rom_public_key)) begin
boot_authorized <= 1'b1;
end else begin
boot_error <= 1'b1;
end
end
end
// If vulnerability discovered (e.g., Bleichenbacher attack):
// - Cannot update verification algorithm
// - Cannot revoke compromised key
// - All devices permanently vulnerable
endmodule
// Vulnerable: Boot ROM code without patch support
// This code is burned into ROM - cannot be changed after manufacturing
#include <stdint.h>
// VULNERABLE: Fixed cryptographic implementation in ROM
// If algorithm has vulnerability, cannot be patched
void rom_sha256(const uint8_t* data, size_t len, uint8_t* hash) {
// SHA-256 implementation
// VULNERABLE: If bug found here, unfixable
}
// VULNERABLE: Secure boot verification in ROM
int rom_verify_firmware(const uint8_t* firmware, size_t size,
const uint8_t* signature) {
// VULNERABLE: Fixed public key in ROM
static const uint8_t rom_public_key[256] = {
// RSA-2048 public key
// Cannot be changed if compromised
};
uint8_t hash[32];
rom_sha256(firmware, size, hash);
// VULNERABLE: RSA verification code in ROM
// Padding vulnerabilities cannot be fixed
return rom_rsa_verify(hash, signature, rom_public_key);
}
// VULNERABLE: Main boot sequence in ROM
void rom_boot(void) {
// 1. Hardware initialization (typically must be in ROM)
init_hardware();
// 2. Load firmware from storage
uint8_t* firmware = load_firmware();
size_t size = get_firmware_size();
uint8_t* signature = get_firmware_signature();
// 3. VULNERABLE: Verification uses potentially buggy ROM code
if (!rom_verify_firmware(firmware, size, signature)) {
// Boot failure
halt();
}
// 4. Execute firmware
// VULNERABLE: No way to apply patches before this point
jump_to_firmware(firmware);
}
Fixed Code
// Fixed: ROM with patch loading capability
module secure_boot_rom_with_patch (
input wire clk,
input wire rst_n,
input wire [11:0] rom_addr,
input wire ariane_boot_sel_i,
// Patch interface
input wire [11:0] patch_addr,
input wire [31:0] patch_data,
input wire patch_valid,
input wire patch_enable, // From fuse or secure register
input wire [2047:0] patch_signature,
output wire [31:0] rom_rdata,
output reg patch_error
);
// Fixed ROM content
reg [31:0] rom_linux [0:4095];
reg [31:0] rom_patch [0:4095]; // FIXED: Patchable RAM overlay
reg [31:0] rom_rdata_linux;
reg [31:0] rom_rdata_patch;
reg patch_loaded;
// FIXED: Fuse bits indicating which addresses are patched
reg [4095:0] patch_mask;
// ROM initialization
initial begin
$readmemh("linux_bootrom.hex", rom_linux);
patch_loaded = 1'b0;
patch_mask = 4096'b0;
end
// FIXED: Patch loading with signature verification
wire patch_sig_valid;
patch_signature_verifier u_patch_verify (
.clk(clk),
.rst_n(rst_n),
.patch_data(patch_data),
.patch_signature(patch_signature),
.signature_valid(patch_sig_valid)
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
patch_loaded <= 1'b0;
patch_error <= 1'b0;
patch_mask <= 4096'b0;
end else if (patch_enable && patch_valid) begin
// FIXED: Only load signed patches
if (patch_sig_valid) begin
rom_patch[patch_addr] <= patch_data;
patch_mask[patch_addr] <= 1'b1;
patch_loaded <= 1'b1;
end else begin
patch_error <= 1'b1;
end
end
end
// ROM read
always @(posedge clk) begin
rom_rdata_linux <= rom_linux[rom_addr];
rom_rdata_patch <= rom_patch[rom_addr];
end
// FIXED: Output patch data if address is patched, else ROM data
wire use_patch = patch_loaded && patch_mask[rom_addr];
// FIXED: Correct boot selection (bug fixed)
wire [31:0] selected_rom_data;
assign selected_rom_data = ariane_boot_sel_i ? rom_rdata_patch : rom_rdata_linux;
// FIXED: Patch overlay
assign rom_rdata = use_patch ? rom_rdata_patch : selected_rom_data;
endmodule
// Fixed: Fuse-based patching for limited updates
module secure_fuse_patchable_rom (
input wire clk,
input wire rst_n,
input wire [11:0] rom_addr,
output wire [31:0] rom_rdata,
// Fuse interface
input wire [31:0] fuse_patch_0, // Hardware fuses - one-time programmable
input wire [31:0] fuse_patch_1,
input wire [31:0] fuse_patch_2,
input wire [31:0] fuse_patch_3,
input wire [11:0] fuse_patch_addr_0,
input wire [11:0] fuse_patch_addr_1,
input wire [11:0] fuse_patch_addr_2,
input wire [11:0] fuse_patch_addr_3,
input wire [3:0] fuse_patch_valid
);
reg [31:0] rom_content [0:4095];
reg [31:0] rom_rdata_reg;
initial begin
$readmemh("bootrom.hex", rom_content);
end
always @(posedge clk) begin
// FIXED: Check if address matches a fuse patch
if (fuse_patch_valid[0] && rom_addr == fuse_patch_addr_0) begin
rom_rdata_reg <= fuse_patch_0;
end else if (fuse_patch_valid[1] && rom_addr == fuse_patch_addr_1) begin
rom_rdata_reg <= fuse_patch_1;
end else if (fuse_patch_valid[2] && rom_addr == fuse_patch_addr_2) begin
rom_rdata_reg <= fuse_patch_2;
end else if (fuse_patch_valid[3] && rom_addr == fuse_patch_addr_3) begin
rom_rdata_reg <= fuse_patch_3;
end else begin
rom_rdata_reg <= rom_content[rom_addr];
end
end
assign rom_rdata = rom_rdata_reg;
// FIXED: Fuses allow limited field patching
// Up to 4 instruction patches via OTP fuses
endmodule
// Fixed: Boot ROM with patch loading capability
#include <stdint.h>
#include <stdbool.h>
// Minimal ROM code - just enough to load and verify patches
// Larger boot code is in flash and can be patched
// Fixed ROM public key for patch verification
static const uint8_t ROM_PATCH_KEY[256] = {
// RSA-2048 key for patch verification only
};
// Patch storage structure
typedef struct {
uint32_t patch_address;
uint32_t patch_size;
uint8_t patch_data[4096];
uint8_t signature[256];
} rom_patch_t;
// FIXED: Verify patch signature before applying
bool rom_verify_patch(const rom_patch_t* patch) {
uint8_t hash[32];
// Hash patch metadata and data
sha256_context ctx;
sha256_init(&ctx);
sha256_update(&ctx, &patch->patch_address, sizeof(patch->patch_address));
sha256_update(&ctx, &patch->patch_size, sizeof(patch->patch_size));
sha256_update(&ctx, patch->patch_data, patch->patch_size);
sha256_final(&ctx, hash);
// Verify signature with ROM key
return rom_rsa_verify(hash, patch->signature, ROM_PATCH_KEY);
}
// FIXED: Patch RAM overlay
static uint8_t patch_overlay[4096];
static uint32_t patch_base_address;
static uint32_t patch_size;
static bool patch_active = false;
// FIXED: Apply verified patch to RAM overlay
bool rom_apply_patch(const rom_patch_t* patch) {
// Verify patch first
if (!rom_verify_patch(patch)) {
return false;
}
// Copy patch to RAM overlay
memcpy(patch_overlay, patch->patch_data, patch->patch_size);
patch_base_address = patch->patch_address;
patch_size = patch->patch_size;
patch_active = true;
return true;
}
// FIXED: ROM read function that checks patch overlay
uint32_t rom_read(uint32_t address) {
// Check if address is in patched region
if (patch_active &&
address >= patch_base_address &&
address < patch_base_address + patch_size) {
// Return patched data
return *(uint32_t*)&patch_overlay[address - patch_base_address];
}
// Return original ROM data
return *(uint32_t*)(ROM_BASE + address);
}
// FIXED: Boot sequence with patch support
void rom_boot(void) {
// 1. Minimal hardware initialization (must be in ROM)
init_critical_hardware();
// 2. FIXED: Load and verify patches from secure storage
rom_patch_t* patch = load_patch_from_storage();
if (patch != NULL) {
if (!rom_apply_patch(patch)) {
// Patch verification failed
// Continue with unpatched ROM
}
}
// 3. Load secondary bootloader (may be patched)
uint8_t* bootloader = load_bootloader();
size_t size = get_bootloader_size();
uint8_t* signature = get_bootloader_signature();
// 4. Verify bootloader (using potentially patched verification)
if (!verify_bootloader(bootloader, size, signature)) {
halt();
}
// 5. Execute bootloader
jump_to_bootloader(bootloader);
}
// FIXED: Fuse-based emergency patch
void apply_fuse_patches(void) {
// Read patch data from OTP fuses
uint32_t fuse_patch_addr = read_fuse(FUSE_PATCH_ADDR);
uint32_t fuse_patch_data = read_fuse(FUSE_PATCH_DATA);
if (fuse_patch_addr != 0xFFFFFFFF) {
// Apply single-instruction fuse patch
// Limited but available for critical fixes
apply_instruction_patch(fuse_patch_addr, fuse_patch_data);
}
}
CVE Examples
- CVE-2020-0069: MediaTek BootROM vulnerability that couldn't be patched, affecting millions of devices.
- CVE-2019-14192: U-Boot ROM vulnerabilities requiring hardware replacement.
- CVE-2021-30900: Apple BootROM (SecureROM) vulnerability in checkm8 exploit.
Related CWEs
- CWE-1329: Reliance on Component That is Not Updateable (parent)
- CWE-1196: Security Flow Issues (category)
- CWE-693: Protection Mechanism Failure (related)
References
- MITRE Corporation. "CWE-1310: Missing Ability to Patch ROM Code." https://cwe.mitre.org/data/definitions/1310.html
- NIST. "Platform Firmware Resiliency Guidelines (SP 800-193)"
- OpenTitan. "ROM and ROM Extension Design"