Failure to Disable Reserved Bits

Description

Failure to Disable Reserved Bits occurs when reserved bits in hardware designs are not disabled before production release. Reserved bits are allocated in register definitions for potential future capabilities and should perform no function in current designs. However, designers may utilize these bits to expedite time-to-market by controlling undocumented features or for debugging during development. If the associated logic remains enabled in production, attackers could use these bits to induce unwanted or unsupported behavior in the hardware, potentially compromising system security.

Risk

Enabled reserved bits have significant security implications. Attackers can discover and exploit undocumented functionality. Debug features may remain accessible in production. Hidden backdoors may be present. Security controls may be bypassable through reserved bits. Undocumented behavior may compromise system integrity. Firmware updates may not address reserved bit vulnerabilities. Security certifications may be invalidated. Behavior may be unpredictable when reserved bits are set.

Solution

Ensure all reserved bits are tied to safe values (typically 0). Remove or disable all logic controlled by reserved bits before production. Document reserved bits clearly as "must be zero" or "must be one". Implement hardware enforcement that ignores writes to reserved bits. Test that setting reserved bits has no effect. Review all register definitions for active reserved bit logic. Audit reserved bit usage during security reviews. Use static analysis to find reserved bit connections. Verify reserved bit handling in silicon validation.

Common Consequences

ImpactDetails
Confidentiality, Integrity, Availability, Access ControlScope: All

Varies By Context - Consequences depend on what capabilities are controlled by the reserved bits. Could range from information disclosure to full system compromise.

Example Code

Vulnerable Code

// Vulnerable: Reserved bits enable hidden functionality

module vulnerable_control_register (
    input wire clk,
    input wire reset_n,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire [3:0] register_address,
    output reg [31:0] read_data,
    output reg gpio_out,
    output reg debug_mode,
    output reg bypass_security
);

    // Control register with "reserved" bits that do something
    // Documented bits:
    //   [0]    - Enable feature A
    //   [1]    - Enable feature B
    //   [7:2]  - Reserved (should be 0)
    //   [15:8] - Configuration value
    //   [31:16]- Reserved (should be 0)

    reg [31:0] control_reg;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            control_reg <= 32'h0;
            gpio_out <= 1'b0;
            debug_mode <= 1'b0;
            bypass_security <= 1'b0;
        end
        else if (write_enable && register_address == 4'b0001) begin
            control_reg <= write_data;

            // VULNERABLE: "Reserved" bits actually do things!

            // Reserved bit [2] secretly enables GPIO
            gpio_out <= write_data[2];

            // Reserved bit [3] enables debug mode
            debug_mode <= write_data[3];

            // Reserved bit [16] bypasses security checks!
            bypass_security <= write_data[16];
        end
    end

    // Another vulnerable pattern: reserved address enables hidden function
    always @(posedge clk) begin
        case (register_address)
            4'b0000: read_data <= status_reg;
            4'b0001: read_data <= control_reg;
            // Address 0x0F is "reserved" but enables backdoor
            4'b1111: gpio_out <= 1'b1;  // Hidden functionality!
            default: read_data <= 32'h0;
        endcase
    end

endmodule

// Vulnerable: Undocumented debug feature via reserved field
module vulnerable_memory_controller (
    input wire clk,
    input wire [31:0] command,
    input wire [31:0] address,
    output reg [31:0] data_out,
    output reg command_complete
);

    // Command format:
    //   [3:0]   - Operation code
    //   [7:4]   - Reserved (should be 0)
    //   [31:8]  - Parameters

    wire [3:0] opcode = command[3:0];
    wire [3:0] reserved = command[7:4];  // "Reserved" bits

    always @(posedge clk) begin
        case (opcode)
            4'h1: begin
                // Normal read operation
                data_out <= memory[address];
                command_complete <= 1'b1;
            end
            4'h2: begin
                // Normal write operation
                memory[address] <= command[31:8];
                command_complete <= 1'b1;
            end
            default: begin
                // VULNERABLE: Reserved bits check for debug backdoor
                if (reserved == 4'hA) begin
                    // Hidden: Dump all memory when reserved = 0xA
                    dump_all_memory();
                end
                if (reserved == 4'hB) begin
                    // Hidden: Bypass access control
                    bypass_acl <= 1'b1;
                end
            end
        endcase
    end

endmodule
// Vulnerable: Firmware with active reserved bit handling

#define CONTROL_REG_ADDR 0x40001000

// Documented register bits
#define CTRL_ENABLE_A    (1 << 0)
#define CTRL_ENABLE_B    (1 << 1)
// Bits 2-7: Reserved
#define CTRL_CONFIG_MASK 0xFF00
// Bits 16-31: Reserved

// VULNERABLE: Hidden functionality in "reserved" bits
#define CTRL_DEBUG_MODE  (1 << 3)   // "Reserved" but active
#define CTRL_BYPASS_SEC  (1 << 16)  // "Reserved" but active

void configure_hardware(uint32_t config) {
    // Write to control register
    // Reserved bits are written too, enabling hidden features
    *(volatile uint32_t*)CONTROL_REG_ADDR = config;
}

// Attacker can exploit by setting reserved bits
void exploit_reserved_bits(void) {
    // Enable undocumented debug mode
    configure_hardware(CTRL_ENABLE_A | CTRL_DEBUG_MODE);

    // Bypass security checks
    configure_hardware(CTRL_ENABLE_A | CTRL_BYPASS_SEC);
}

Fixed Code

// Fixed: Reserved bits are properly disabled

module secure_control_register (
    input wire clk,
    input wire reset_n,
    input wire [31:0] write_data,
    input wire write_enable,
    input wire [3:0] register_address,
    output reg [31:0] read_data,
    output reg feature_a_enable,
    output reg feature_b_enable,
    output reg [7:0] config_value
);

    // Control register with truly reserved bits
    // Documented bits:
    //   [0]    - Enable feature A
    //   [1]    - Enable feature B
    //   [7:2]  - Reserved (MUST BE ZERO)
    //   [15:8] - Configuration value
    //   [31:16]- Reserved (MUST BE ZERO)

    reg [31:0] control_reg;

    // Mask for writable bits only
    localparam WRITABLE_MASK = 32'h0000FF03;

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            control_reg <= 32'h0;
            feature_a_enable <= 1'b0;
            feature_b_enable <= 1'b0;
            config_value <= 8'h0;
        end
        else if (write_enable && register_address == 4'b0001) begin
            // Only write to documented bits - mask out reserved
            control_reg <= write_data & WRITABLE_MASK;

            // Only use documented bits
            feature_a_enable <= write_data[0];
            feature_b_enable <= write_data[1];
            config_value <= write_data[15:8];

            // Reserved bits [7:2] and [31:16] are ignored
            // No hidden functionality
        end
    end

    always @(*) begin
        case (register_address)
            4'b0000: read_data = status_reg;
            4'b0001: read_data = control_reg;
            // Reserved addresses return zero, do nothing
            default: read_data = 32'h0;
        endcase
        // No hidden functionality on any address
    end

endmodule

// Fixed: No debug backdoors in reserved fields
module secure_memory_controller (
    input wire clk,
    input wire reset_n,
    input wire [31:0] command,
    input wire [31:0] address,
    input wire command_valid,
    output reg [31:0] data_out,
    output reg command_complete,
    output reg command_error
);

    // Command format:
    //   [3:0]   - Operation code
    //   [7:4]   - Reserved (MUST BE ZERO)
    //   [31:8]  - Parameters

    wire [3:0] opcode = command[3:0];
    wire [3:0] reserved = command[7:4];

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            data_out <= 32'h0;
            command_complete <= 1'b0;
            command_error <= 1'b0;
        end
        else if (command_valid) begin
            // Check reserved bits are zero
            if (reserved != 4'h0) begin
                // Reserved bits must be zero - reject command
                command_error <= 1'b1;
                command_complete <= 1'b1;
            end
            else begin
                command_error <= 1'b0;
                case (opcode)
                    4'h1: begin
                        // Normal read operation
                        data_out <= memory[address];
                        command_complete <= 1'b1;
                    end
                    4'h2: begin
                        // Normal write operation
                        memory[address] <= command[31:8];
                        command_complete <= 1'b1;
                    end
                    default: begin
                        // Unknown opcode - error
                        command_error <= 1'b1;
                        command_complete <= 1'b1;
                    end
                endcase
            end
        end
    end

    // No hidden functionality anywhere

endmodule

// Fixed: Production version with debug removed
module secure_control_register_production (
    input wire clk,
    input wire reset_n,
    input wire [31:0] write_data,
    input wire write_enable,
    output reg [31:0] control_reg
);

    // Production-only bits
    localparam PRODUCTION_MASK = 32'h0000FF03;

    // Debug functionality completely removed in production
    // Not just disabled - the logic doesn't exist

    `ifdef PRODUCTION
        // Production build: no debug logic synthesized
        always @(posedge clk or negedge reset_n) begin
            if (!reset_n)
                control_reg <= 32'h0;
            else if (write_enable)
                control_reg <= write_data & PRODUCTION_MASK;
        end
    `else
        // Development build: debug available
        // This code is NOT included in production synthesis
        always @(posedge clk or negedge reset_n) begin
            if (!reset_n)
                control_reg <= 32'h0;
            else if (write_enable)
                control_reg <= write_data;
        end
    `endif

endmodule
// Fixed: Firmware properly handles reserved bits

#define CONTROL_REG_ADDR 0x40001000

// Documented register bits
#define CTRL_ENABLE_A    (1 << 0)
#define CTRL_ENABLE_B    (1 << 1)
#define CTRL_CONFIG_MASK 0x0000FF00

// Mask of all valid bits
#define CTRL_VALID_MASK  (CTRL_ENABLE_A | CTRL_ENABLE_B | CTRL_CONFIG_MASK)

// Reserved bits - for documentation only
// These bits MUST be written as zero
#define CTRL_RESERVED_MASK (~CTRL_VALID_MASK)

void configure_hardware(uint32_t config) {
    // Mask out reserved bits before writing
    uint32_t safe_config = config & CTRL_VALID_MASK;

    // Verify no reserved bits were set (optional check)
    if (config & CTRL_RESERVED_MASK) {
        log_warning("Attempt to set reserved bits: 0x%08x", config);
    }

    // Write only valid bits
    *(volatile uint32_t*)CONTROL_REG_ADDR = safe_config;
}

uint32_t read_hardware_config(void) {
    uint32_t value = *(volatile uint32_t*)CONTROL_REG_ADDR;

    // Mask out reserved bits when reading
    return value & CTRL_VALID_MASK;
}

// Validation function for security audit
bool validate_no_reserved_bit_effects(void) {
    uint32_t original = read_hardware_config();

    // Try setting each reserved bit
    for (int bit = 0; bit < 32; bit++) {
        if (CTRL_RESERVED_MASK & (1 << bit)) {
            // This is a reserved bit - verify it has no effect
            *(volatile uint32_t*)CONTROL_REG_ADDR = original | (1 << bit);

            // Check system state didn't change unexpectedly
            if (detect_state_change()) {
                log_error("Reserved bit %d has effect!", bit);
                return false;
            }
        }
    }

    // Restore original
    *(volatile uint32_t*)CONTROL_REG_ADDR = original;
    return true;
}

CVE Examples

Reserved bit vulnerabilities have been found in various hardware designs, including processors and embedded controllers, where undocumented bits enable debug or bypass functionality.


  • CWE-710: Improper Adherence to Coding Standards (parent)
  • CWE-1199: General Circuit and Logic Design Concerns (category member)
  • CWE-1242: Inclusion of Undocumented Features or Chicken Bits (related)

References

  1. MITRE Corporation. "CWE-1209: Failure to Disable Reserved Bits." https://cwe.mitre.org/data/definitions/1209.html
  2. Hardware Security Best Practices
  3. Production vs. Debug Build Guidelines