Policy Privileges are not Assigned Consistently Between Control and Data Agents

Description

Policy Privileges are not Assigned Consistently Between Control and Data Agents occurs when hardware-enforced access control fails to properly account for privilege discrepancies between control and write policies affecting resource access. Hardware systems often implement multi-level access policies to control resource access including configuration and encryption keys. When control policies permit both direct resource access and policy modifications, resources included in control policies but excluded from write policies create vulnerabilities. An untrusted agent could exploit this gap to insert itself into write policy registers, potentially gaining unauthorized write access.

Risk

Inconsistent policy privileges have severe security implications. Untrusted agents may grant themselves access. Write policies can be modified through control access. Security configurations may be compromised. Encryption keys may be overwritten. Privilege escalation becomes possible. Protection mechanisms can be bypassed. System security may be undermined. Access control becomes ineffective.

Solution

Access control policies require comprehensive testing during both pre-silicon and post-silicon development phases to ensure consistent privilege assignment across all policy levels. Ensure that agents with control access to a resource also have consistent data access permissions. Implement privilege separation between policy modification and resource access. Audit policy consistency during security reviews.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Memory - Inconsistent policies may allow unauthorized data access.
IntegrityScope: Integrity

Modify Memory - Agents may gain write access through policy manipulation.
Access ControlScope: Access Control

Bypass Protection Mechanism - Policy inconsistencies enable privilege escalation.

Example Code

Vulnerable Code

// Vulnerable: Inconsistent control and data policies

module vulnerable_aes_policy (
    input wire clk,
    input wire reset_n,
    // Agent interface
    input wire [3:0] agent_id,
    input wire [7:0] reg_addr,
    input wire [31:0] reg_write_data,
    input wire reg_write,
    input wire reg_read,
    output reg [31:0] reg_read_data,
    output reg access_denied
);

    // AES key storage
    reg [127:0] aes_key;

    // Policy registers
    reg [15:0] aes_key_control_policy;  // Who can modify policies
    reg [15:0] aes_key_read_policy;     // Who can read key
    reg [15:0] aes_key_write_policy;    // Who can write key

    // Register addresses
    parameter AES_KEY_REG = 8'h00;
    parameter CONTROL_POLICY_REG = 8'h10;
    parameter READ_POLICY_REG = 8'h14;
    parameter WRITE_POLICY_REG = 8'h18;

    // VULNERABLE: Initial policy assignment
    initial begin
        // Agent 0: Secure Master - full access
        // Agent 1: Crypto Engine - read/write key
        // Agent 2: Boot ROM - read key only
        // Agent 3: Untrusted Debug - SHOULD have no access

        aes_key_control_policy = 16'b0000_0000_0000_1111;
        // Agents 0,1,2,3 can modify policies
        // VULNERABLE: Agent 3 (untrusted) has control access!

        aes_key_read_policy = 16'b0000_0000_0000_0111;
        // Agents 0,1,2 can read key

        aes_key_write_policy = 16'b0000_0000_0000_0011;
        // Agents 0,1 can write key
        // Agent 3 cannot write key directly
    end

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            access_denied <= 1'b0;
        end
        else if (reg_write) begin
            access_denied <= 1'b0;

            case (reg_addr)
                AES_KEY_REG: begin
                    // Check write policy
                    if (aes_key_write_policy[agent_id]) begin
                        aes_key <= reg_write_data;
                    end
                    else begin
                        access_denied <= 1'b1;
                    end
                end

                WRITE_POLICY_REG: begin
                    // VULNERABLE: Check control policy for policy modification
                    if (aes_key_control_policy[agent_id]) begin
                        // Agent 3 can reach here!
                        // Agent 3 sets bit 3 to give itself write access
                        aes_key_write_policy <= reg_write_data;
                    end
                    else begin
                        access_denied <= 1'b1;
                    end
                end

                // Similar vulnerability for other policy registers
            endcase
        end
    end

    // Attack by Agent 3:
    // 1. Agent 3 has control policy access (bit 3 set)
    // 2. Agent 3 writes to WRITE_POLICY_REG
    // 3. Agent 3 sets bit 3 in write policy
    // 4. Now Agent 3 has write access to AES key!

endmodule
// Vulnerable: Software policy with inconsistent privileges

#include <stdint.h>

typedef struct {
    uint32_t control_mask;   // Who can modify policies
    uint32_t read_mask;      // Who can read resource
    uint32_t write_mask;     // Who can write resource
} resource_policy_t;

// VULNERABLE: Inconsistent policy assignment
resource_policy_t secret_key_policy = {
    .control_mask = 0x0000000F,  // Agents 0-3 can modify policies
    .read_mask    = 0x00000007,  // Agents 0-2 can read
    .write_mask   = 0x00000003,  // Agents 0-1 can write
    // Agent 3 has control but not read/write
    // This inconsistency is exploitable
};

int vulnerable_modify_policy(uint32_t agent_id, resource_policy_t* policy,
                             uint32_t policy_type, uint32_t new_value) {
    // Check if agent has control access
    if (!(policy->control_mask & (1 << agent_id))) {
        return -EACCES;
    }

    // VULNERABLE: Agent with control can modify any policy
    switch (policy_type) {
        case POLICY_CONTROL:
            policy->control_mask = new_value;
            break;
        case POLICY_READ:
            policy->read_mask = new_value;
            break;
        case POLICY_WRITE:
            // Agent 3 can set this to grant itself write access!
            policy->write_mask = new_value;
            break;
    }

    return 0;
}

// Attack:
// 1. Agent 3 calls vulnerable_modify_policy(..., POLICY_WRITE, 0x0000000F)
// 2. Now Agent 3 has write access to secret key

Fixed Code

// Fixed: Consistent policy privileges

module secure_aes_policy (
    input wire clk,
    input wire reset_n,
    input wire [3:0] agent_id,
    input wire [7:0] reg_addr,
    input wire [31:0] reg_write_data,
    input wire reg_write,
    input wire reg_read,
    output reg [31:0] reg_read_data,
    output reg access_denied,
    output reg policy_violation
);

    // AES key storage
    reg [127:0] aes_key;

    // Policy registers
    reg [15:0] aes_key_control_policy;
    reg [15:0] aes_key_read_policy;
    reg [15:0] aes_key_write_policy;

    // FIXED: Master policy - only secure master can modify
    parameter SECURE_MASTER = 4'd0;

    // Register addresses
    parameter AES_KEY_REG = 8'h00;
    parameter CONTROL_POLICY_REG = 8'h10;
    parameter READ_POLICY_REG = 8'h14;
    parameter WRITE_POLICY_REG = 8'h18;

    // FIXED: Consistent policy assignment
    initial begin
        // Only trusted agents have control access
        aes_key_control_policy = 16'b0000_0000_0000_0001;
        // Only Agent 0 (Secure Master) can modify policies

        aes_key_read_policy = 16'b0000_0000_0000_0111;
        // Agents 0,1,2 can read key

        aes_key_write_policy = 16'b0000_0000_0000_0011;
        // Agents 0,1 can write key
    end

    // FIXED: Validate policy consistency
    function automatic is_policy_consistent;
        input [15:0] new_control;
        input [15:0] current_read;
        input [15:0] current_write;
        begin
            // Control policy must be subset of read AND write
            // Or: agents with control must also have data access
            is_policy_consistent =
                ((new_control & ~current_read) == 16'h0) &&
                ((new_control & ~current_write) == 16'h0);
        end
    endfunction

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            access_denied <= 1'b0;
            policy_violation <= 1'b0;
        end
        else if (reg_write) begin
            access_denied <= 1'b0;
            policy_violation <= 1'b0;

            case (reg_addr)
                AES_KEY_REG: begin
                    if (aes_key_write_policy[agent_id]) begin
                        aes_key <= reg_write_data;
                    end
                    else begin
                        access_denied <= 1'b1;
                    end
                end

                CONTROL_POLICY_REG: begin
                    // FIXED: Only secure master can modify control policy
                    if (agent_id == SECURE_MASTER) begin
                        // FIXED: Validate new policy is consistent
                        if (is_policy_consistent(reg_write_data,
                                                 aes_key_read_policy,
                                                 aes_key_write_policy)) begin
                            aes_key_control_policy <= reg_write_data;
                        end
                        else begin
                            policy_violation <= 1'b1;
                        end
                    end
                    else begin
                        access_denied <= 1'b1;
                    end
                end

                WRITE_POLICY_REG: begin
                    // FIXED: Only secure master can modify write policy
                    if (agent_id == SECURE_MASTER) begin
                        aes_key_write_policy <= reg_write_data;
                    end
                    else begin
                        access_denied <= 1'b1;
                    end
                end

                READ_POLICY_REG: begin
                    // FIXED: Only secure master can modify read policy
                    if (agent_id == SECURE_MASTER) begin
                        aes_key_read_policy <= reg_write_data;
                    end
                    else begin
                        access_denied <= 1'b1;
                    end
                end
            endcase
        end
    end

endmodule

// Alternative: Hierarchical policy with privilege levels
module secure_hierarchical_policy (
    input wire clk,
    input wire reset_n,
    input wire [3:0] agent_id,
    input wire [1:0] agent_privilege,  // 0=user, 1=supervisor, 2=hypervisor, 3=secure
    input wire [7:0] reg_addr,
    input wire [31:0] reg_write_data,
    input wire reg_write,
    output reg access_denied
);

    // FIXED: Privilege required for each operation
    parameter CONTROL_PRIV = 2'd3;  // Secure only
    parameter WRITE_PRIV = 2'd2;    // Hypervisor+
    parameter READ_PRIV = 2'd1;     // Supervisor+

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            access_denied <= 1'b0;
        end
        else if (reg_write) begin
            case (reg_addr)
                POLICY_REG: begin
                    // FIXED: Only highest privilege can modify policies
                    if (agent_privilege >= CONTROL_PRIV) begin
                        // Update policy
                    end
                    else begin
                        access_denied <= 1'b1;
                    end
                end

                DATA_REG: begin
                    // FIXED: Write requires appropriate privilege
                    if (agent_privilege >= WRITE_PRIV) begin
                        // Write data
                    end
                    else begin
                        access_denied <= 1'b1;
                    end
                end
            endcase
        end
    end

endmodule
// Fixed: Software policy with consistent privileges

#include <stdint.h>
#include <stdbool.h>

typedef struct {
    uint32_t control_mask;
    uint32_t read_mask;
    uint32_t write_mask;
    bool locked;  // FIXED: Policy can be locked
} resource_policy_t;

// FIXED: Validate policy consistency
static bool is_policy_consistent(const resource_policy_t* policy,
                                 uint32_t new_control) {
    // Control agents must have corresponding data access
    // to prevent privilege escalation

    // Every agent with control must have read access
    if ((new_control & ~policy->read_mask) != 0) {
        return false;
    }

    // Every agent with control must have write access
    // (or control must only be for reading policies)
    if ((new_control & ~policy->write_mask) != 0) {
        return false;
    }

    return true;
}

// FIXED: Secure policy modification
int secure_modify_policy(uint32_t agent_id, uint32_t agent_privilege,
                         resource_policy_t* policy,
                         uint32_t policy_type, uint32_t new_value) {
    // FIXED: Policy modification requires highest privilege
    if (agent_privilege < PRIVILEGE_SECURE) {
        log_access_denied("Policy modification requires secure privilege");
        return -EPERM;
    }

    // FIXED: Check if policy is locked
    if (policy->locked) {
        log_access_denied("Policy is locked");
        return -EPERM;
    }

    switch (policy_type) {
        case POLICY_CONTROL:
            // FIXED: Validate consistency before applying
            if (!is_policy_consistent(policy, new_value)) {
                log_error("Inconsistent control policy rejected");
                return -EINVAL;
            }
            policy->control_mask = new_value;
            break;

        case POLICY_READ:
            // FIXED: Ensure control is subset of read
            if ((policy->control_mask & ~new_value) != 0) {
                log_error("Read policy would exclude control agents");
                return -EINVAL;
            }
            policy->read_mask = new_value;
            break;

        case POLICY_WRITE:
            // FIXED: Ensure control is subset of write
            if ((policy->control_mask & ~new_value) != 0) {
                log_error("Write policy would exclude control agents");
                return -EINVAL;
            }
            policy->write_mask = new_value;
            break;

        case POLICY_LOCK:
            // FIXED: Allow locking policy
            policy->locked = true;
            break;

        default:
            return -EINVAL;
    }

    log_policy_change(agent_id, policy_type, new_value);
    return 0;
}

// FIXED: Factory function for consistent policies
resource_policy_t create_consistent_policy(uint32_t trusted_agents,
                                           uint32_t read_agents,
                                           uint32_t write_agents) {
    resource_policy_t policy;

    // Ensure consistency at creation time
    // Trusted agents must be subset of both read and write
    policy.read_mask = read_agents | trusted_agents;
    policy.write_mask = write_agents | trusted_agents;
    policy.control_mask = trusted_agents;  // Only trusted can modify
    policy.locked = false;

    return policy;
}

CVE Examples

Inconsistent policy vulnerabilities have been found in SoC designs where untrusted agents could exploit control access to grant themselves data access permissions they should not have.


  • CWE-266: Incorrect Privilege Assignment (parent)
  • CWE-1267: Policy Uses Obsolete Encoding (related)
  • CWE-1259: Improper Restriction of Security Token Assignment (related)
  • CAPEC-180: Exploiting Incorrectly Configured Access Control Levels (attack pattern)

References

  1. MITRE Corporation. "CWE-1268: Policy Privileges are not Assigned Consistently Between Control and Data Agents." https://cwe.mitre.org/data/definitions/1268.html
  2. ARM. "TrustZone Security Extensions"
  3. RISC-V. "Physical Memory Protection Specification"