Audit the following candidate code to determine whether it presents a real vulnerability.
This sample may come from a noisy retrieval or transformation stage, so reason from the supplied code and context rather than assuming the label.

Repository: github.com/espressif/esp-idf
Language: C
File: components/bt/host/bluedroid/stack/avrc/avrc_opt.c
Function: avrc_vendor_msg

Call path: AVRC_VendorCmd (components/bt/host/bluedroid/stack/avrc/avrc_opt.c:198) → avrc_vendor_msg (components/bt/host/bluedroid/stack/avrc/avrc_opt.c:49) → osi_malloc (components/bt/host/bluedroid/osi/allocator.c) → memcpy (string.h)

### Primary Function

```c
static BT_HDR   *avrc_vendor_msg(tAVRC_MSG_VENDOR *p_msg)
{
    BT_HDR  *p_cmd = NULL;
    UINT8   *p_data;

/*
  A vendor dependent command consists of at least of:
  - A BT_HDR, plus
  - AVCT_MSG_OFFSET, plus
  - 3 bytes for ctype, subunit_type and op_vendor, plus
  - 3 bytes for company_id
*/
#define AVRC_MIN_VENDOR_CMD_LEN (BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE)

    if (!p_msg) {
        return NULL;
    }

#if AVRC_METADATA_INCLUDED == TRUE
    if ((AVRC_META_CMD_BUF_SIZE > AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len) &&
        ((p_cmd = (BT_HDR *) osi_malloc(AVRC_META_CMD_BUF_SIZE)) != NULL))
#else
    if ((AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)) &&
        (p_cmd = (BT_HDR *) osi_malloc(AVRC_CMD_BUF_SIZE)) != NULL)
#endif
    {
        p_cmd->offset   = AVCT_MSG_OFFSET;
        p_data          = (UINT8 *)(p_cmd + 1) + p_cmd->offset;
        *p_data++       = (p_msg->hdr.ctype & AVRC_CTYPE_MASK);
        *p_data++       = (p_msg->hdr.subunit_type << AVRC_SUBTYPE_SHIFT) | p_msg->hdr.subunit_id;
        *p_data++       = AVRC_OP_VENDOR;
        AVRC_CO_ID_TO_BE_STREAM(p_data, p_msg->company_id);
        if (p_msg->vendor_len && p_msg->p_vendor_data) {
            memcpy(p_data, p_msg->p_vendor_data, p_msg->vendor_len);
        }
        p_cmd->len  = (UINT16) (p_data + p_msg->vendor_len - (UINT8 *)(p_cmd + 1) - p_cmd->offset);
        p_cmd->layer_specific   = AVCT_DATA_CTRL;
    }
    return p_cmd;
}
```

### Cross-File Context

[AVRC_MIN_VENDOR_CMD_LEN — macro — components/bt/host/bluedroid/stack/avrc/avrc_opt.c:61]
AVRC_MIN_VENDOR_CMD_LEN → (BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE)  (components/bt/host/bluedroid/stack/avrc/avrc_opt.c:61)

[AVRC_VENDOR_HDR_SIZE — macro — components/bt/host/bluedroid/stack/avrc/include/avrc_int.h:82]
AVRC_VENDOR_HDR_SIZE → 6 /* ctype, subunit*, opcode, CO_ID */  (components/bt/host/bluedroid/stack/avrc/include/avrc_int.h:82)

[AVCT_MSG_OFFSET — macro — components/bt/host/bluedroid/stack/include/stack/avct_api.h:63]
AVCT_MSG_OFFSET → 15  (components/bt/host/bluedroid/stack/include/stack/avct_api.h:63)

[BT_HDR_SIZE — macro — components/bt/host/bluedroid/stack/include/stack/bt_types.h:204]
BT_HDR_SIZE → (sizeof (BT_HDR))  (components/bt/host/bluedroid/stack/include/stack/bt_types.h:204)

[BT_HDR — struct — components/bt/host/bluedroid/stack/include/stack/bt_types.h:185-195]
```c
typedef struct {
    uint16_t          event;
    uint16_t          len;
    uint16_t          offset;
    uint16_t          layer_specific;
    uint8_t           data[];
} BT_HDR;
```

[AVRC_CMD_BUF_SIZE — macro — components/bt/host/bluedroid/common/include/common/bt_target.h:898]
AVRC_CMD_BUF_SIZE → 288  (components/bt/host/bluedroid/common/include/common/bt_target.h:898)

[AVRC_META_CMD_BUF_SIZE — macro — components/bt/host/bluedroid/common/include/common/bt_target.h:903]
AVRC_META_CMD_BUF_SIZE → BT_SMALL_BUFFER_SIZE  (components/bt/host/bluedroid/common/include/common/bt_target.h:903)

[tAVRC_MSG_VENDOR — struct — components/bt/host/bluedroid/stack/include/stack/avrc_defs.h:836-841]
```c
typedef struct {
    tAVRC_HDR   hdr;        /* Message header. */
    UINT32      company_id; /* Company identifier. */
    UINT8      *p_vendor_data;/* Pointer to vendor dependent data. */
    UINT16      vendor_len; /* Length in bytes of vendor dependent data. */
} tAVRC_MSG_VENDOR;
```

[AVRC_CO_ID_TO_BE_STREAM — macro — components/bt/host/bluedroid/stack/avrc/include/avrc_int.h:74]
AVRC_CO_ID_TO_BE_STREAM → #define AVRC_CO_ID_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); }  (components/bt/host/bluedroid/stack/avrc/include/avrc_int.h:74)

[osi_malloc — helper — components/bt/host/bluedroid/osi/allocator.h]
```c
void *osi_malloc(size_t size);
```

Control flow: The function begins with a NULL check on the input pointer p_msg, returning NULL if invalid. It then proceeds through a conditional compilation block (#if AVRC_METADATA_INCLUDED) that checks whether the appropriate buffer size constant (AVRC_META_CMD_BUF_SIZE or AVRC_CMD_BUF_SIZE) exceeds the minimum required length (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len) and allocates memory via osi_malloc. If the check fails or allocation returns NULL, the function returns NULL. On successful allocation, the function sets the offset in BT_HDR, writes the 3-byte protocol header (ctype, subunit, opcode), writes the 3-byte company_id via AVRC_CO_ID_TO_BE_STREAM, conditionally copies vendor data with memcpy if vendor_len is non-zero and p_vendor_data is non-NULL, calculates the final message length, sets layer_specific, and returns the constructed BT_HDR pointer.

Data flow: Input tAVRC_MSG_VENDOR contains hdr (with ctype and subunit fields), company_id (UINT32), p_vendor_data (pointer), and vendor_len (UINT16). The vendor_len value flows into the buffer size comparison against AVRC_MIN_VENDOR_CMD_LEN and the selected buffer size constant. The hdr.ctype and hdr.subunit_type/subunit_id flow into the first two bytes of the data region. company_id flows through AVRC_CO_ID_TO_BE_STREAM as three bytes. p_vendor_data is memcpy'd with vendor_len as the byte count. The output is a BT_HDR structure with offset set to AVCT_MSG_OFFSET, len set to the computed data length, and layer_specific set to AVCT_DATA_CTRL.

Verdict: Not Vulnerable

Reviewed region:
[components/bt/host/bluedroid/stack/avrc/avrc_opt.c]
```c
static BT_HDR   *avrc_vendor_msg(tAVRC_MSG_VENDOR *p_msg)
{
    BT_HDR  *p_cmd = NULL;
    UINT8   *p_data;

/*
  A vendor dependent command consists of at least of:
  - A BT_HDR, plus
  - AVCT_MSG_OFFSET, plus
  - 3 bytes for ctype, subunit_type and op_vendor, plus
  - 3 bytes for company_id
*/
#define AVRC_MIN_VENDOR_CMD_LEN (BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE)

    if (!p_msg) {
        return NULL;
    }

#if AVRC_METADATA_INCLUDED == TRUE
    if ((AVRC_META_CMD_BUF_SIZE > AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len) &&
        ((p_cmd = (BT_HDR *) osi_malloc(AVRC_META_CMD_BUF_SIZE)) != NULL))
#else
    if ((AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)) &&
        (p_cmd = (BT_HDR *) osi_malloc(AVRC_CMD_BUF_SIZE)) != NULL)
#endif
    {
        p_cmd->offset   = AVCT_MSG_OFFSET;
        p_data          = (UINT8 *)(p_cmd + 1) + p_cmd->offset;
        *p_data++       = (p_msg->hdr.ctype & AVRC_CTYPE_MASK);
        *p_data++       = (p_msg->hdr.subunit_type << AVRC_SUBTYPE_SHIFT) | p_msg->hdr.subunit_id;
        *p_data++       = AVRC_OP_VENDOR;
        AVRC_CO_ID_TO_BE_STREAM(p_data, p_msg->company_id);
        if (p_msg->vendor_len && p_msg->p_vendor_data) {
            memcpy(p_data, p_msg->p_vendor_data, p_msg->vendor_len);
        }
        p_cmd->len  = (UINT16) (p_data + p_msg->vendor_len - (UINT8 *)(p_cmd + 1) - p_cmd->offset);
        p_cmd->layer_specific   = AVCT_DATA_CTRL;
    }
    return p_cmd;
}
```

Why it is not vulnerable: The buffer allocation is guarded by a comparison that verifies the chosen buffer size constant (AVRC_META_CMD_BUF_SIZE or AVRC_CMD_BUF_SIZE) is strictly greater than AVRC_MIN_VENDOR_CMD_LEN plus p_msg->vendor_len before any allocation or write occurs. AVRC_MIN_VENDOR_CMD_LEN is correctly defined as BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE, where AVRC_VENDOR_HDR_SIZE is 6 bytes, accounting for the 3-byte opcode header plus 3-byte company_id written before the vendor data. The data pointer is initialized to (p_cmd + 1) + AVCT_MSG_OFFSET, which is offset 15 within the allocated buffer. All writes — the 3 header bytes, 3 company_id bytes, and vendor_len bytes — occur within the bounds validated by the pre-allocation check. The memcpy is additionally guarded by the condition (p_msg->vendor_len && p_msg->p_vendor_data), preventing writes when vendor_len is zero or the source pointer is NULL.

Security invariants:
- The allocated buffer must be large enough to hold BT_HDR, AVCT_MSG_OFFSET padding, AVRC_VENDOR_HDR_SIZE header bytes, and vendor_len data bytes. Enforced by the check (AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)) before allocation.
- AVRC_MIN_VENDOR_CMD_LEN must account for all fixed-size overhead. Enforced by its definition as (BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE) where AVRC_VENDOR_HDR_SIZE is 6 bytes matching the 3-byte header plus 3-byte company_id.
- All writes to p_data must stay within the allocated buffer bounds. Enforced because the pre-check ensures the total allocated size exceeds the sum of all offsets and data sizes, and writes are limited to 3 + 3 + vendor_len bytes starting from offset 15.
- memcpy source pointer must not be NULL. Enforced by the guard (p_msg->vendor_len && p_msg->p_vendor_data) before the memcpy call.
- Input pointer p_msg must not be NULL. Enforced by the initial check (if (!p_msg)) at function entry.
- osi_malloc must not return NULL before dereferencing p_cmd. Enforced by checking ((p_cmd = (BT_HDR *) osi_malloc(...)) != NULL) in the allocation condition.

Invariant verification:
- Buffer size validation accounts for minimum header overhead: holds=true. Evidence: AVRC_MIN_VENDOR_CMD_LEN is defined as (BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE), and AVRC_VENDOR_HDR_SIZE is 6 bytes, matching the 3 bytes written for ctype/subunit/opcode plus 3 bytes written for company_id via AVRC_CO_ID_TO_BE_STREAM.
- Pre-allocation size check prevents buffer overflow: holds=true. Evidence: The condition (AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)) ensures the constant buffer size is sufficient for the fixed overhead plus variable vendor data before osi_malloc is called.
- Data pointer correctly positioned within allocated buffer: holds=true. Evidence: p_cmd->offset is set to AVCT_MSG_OFFSET (15), and p_data is computed as (UINT8 *)(p_cmd + 1) + p_cmd->offset, positioning writes at byte 15 within the allocation.
- memcpy bounds are safe relative to allocation: holds=true. Evidence: memcpy copies p_msg->vendor_len bytes starting from p_data after 6 bytes of header writes. The pre-check guarantees AVRC_CMD_BUF_SIZE > AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len, so the total write (15 offset + 6 header + vendor_len) stays within the allocated AVRC_CMD_BUF_SIZE bytes.
- Null pointer protection for input and allocation: holds=true. Evidence: The function checks (!p_msg) at entry and verifies (p_cmd = (BT_HDR *) osi_malloc(...)) != NULL in the allocation condition before any dereference of p_cmd.
- Vendor data source pointer validity: holds=true. Evidence: The memcpy is guarded by (p_msg->vendor_len && p_msg->p_vendor_data), ensuring p_vendor_data is non-NULL and vendor_len is non-zero before copying.

Residual risk notes: The size comparison uses strict greater-than (>) rather than greater-than-or-equal (>=), which requires the buffer to be strictly larger than the minimum needed; this is safe but slightly more restrictive. The vendor_len field is UINT16, so its maximum value is 65535, which could cause the comparison check to fail legitimately when the vendor data exceeds the fixed buffer capacity (288 bytes). No overflow concerns in the addition because vendor_len is bounded and the comparison would fail before allocation in edge cases. The function relies on the caller to free the allocated BT_HDR, which is standard but not enforced within this function.

Control flow: The function first validates that p_msg is non-NULL, returning NULL if not. It then checks whether the configured buffer size (AVRC_CMD_BUF_SIZE or AVRC_META_CMD_BUF_SIZE) is strictly greater than AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len. If the check passes, osi_malloc allocates the buffer. On successful allocation, the function writes the AVRC vendor header fields (ctype, subunit_type/id, opcode, company_id) byte-by-byte, then copies vendor data via memcpy, computes the payload length, sets layer_specific, and returns the BT_HDR pointer. If the size check fails or allocation returns NULL, the function returns NULL.

Data flow: Input p_msg (tAVRC_MSG_VENDOR) provides hdr fields, company_id, p_vendor_data pointer, and vendor_len. The vendor_len value is used both in the pre-allocation size guard and as the length parameter for memcpy. The buffer is allocated via osi_malloc with size AVRC_CMD_BUF_SIZE (288) or AVRC_META_CMD_BUF_SIZE. Data is written sequentially: 1 byte ctype, 1 byte subunit, 1 byte opcode, 3 bytes company_id (via AVRC_CO_ID_TO_BE_STREAM macro), then vendor_len bytes via memcpy. The output BT_HDR has its len field set to 6 + vendor_len, offset set to AVCT_MSG_OFFSET (15), and layer_specific set to AVCT_DATA_CTRL.

Verdict: Not Vulnerable

Reviewed region:
[components/bt/host/bluedroid/stack/avrc/avrc_opt.c]
```c
static BT_HDR   *avrc_vendor_msg(tAVRC_MSG_VENDOR *p_msg)
{
    BT_HDR  *p_cmd = NULL;
    UINT8   *p_data;

/*
  A vendor dependent command consists of at least of:
  - A BT_HDR, plus
  - AVCT_MSG_OFFSET, plus
  - 3 bytes for ctype, subunit_type and op_vendor, plus
  - 3 bytes for company_id
*/
#define AVRC_MIN_VENDOR_CMD_LEN (BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE)

    if (!p_msg) {
        return NULL;
    }

#if AVRC_METADATA_INCLUDED == TRUE
    if ((AVRC_META_CMD_BUF_SIZE > AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len) &&
        ((p_cmd = (BT_HDR *) osi_malloc(AVRC_META_CMD_BUF_SIZE)) != NULL))
#else
    if ((AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)) &&
        (p_cmd = (BT_HDR *) osi_malloc(AVRC_CMD_BUF_SIZE)) != NULL)
#endif
    {
        p_cmd->offset   = AVCT_MSG_OFFSET;
        p_data          = (UINT8 *)(p_cmd + 1) + p_cmd->offset;
        *p_data++       = (p_msg->hdr.ctype & AVRC_CTYPE_MASK);
        *p_data++       = (p_msg->hdr.subunit_type << AVRC_SUBTYPE_SHIFT) | p_msg->hdr.subunit_id;
        *p_data++       = AVRC_OP_VENDOR;
        AVRC_CO_ID_TO_BE_STREAM(p_data, p_msg->company_id);
        if (p_msg->vendor_len && p_msg->p_vendor_data) {
            memcpy(p_data, p_msg->p_vendor_data, p_msg->vendor_len);
        }
        p_cmd->len  = (UINT16) (p_data + p_msg->vendor_len - (UINT8 *)(p_cmd + 1) - p_cmd->offset);
        p_cmd->layer_specific   = AVCT_DATA_CTRL;
    }
    return p_cmd;
}
```

Why it is not vulnerable: The memcpy operation is properly bounded. The pre-allocation check `AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)` ensures that the buffer size (288 bytes) strictly exceeds the total data that will be written (AVRC_MIN_VENDOR_CMD_LEN + vendor_len = 29 + vendor_len). Since AVRC_MIN_VENDOR_CMD_LEN = BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE = 8 + 15 + 6 = 29, the check guarantees vendor_len <= 258. The write position starts at offset 23 (BT_HDR_SIZE + AVCT_MSG_OFFSET = 8 + 15), and after writing 6 header bytes, memcpy writes vendor_len bytes ending at position 29 + vendor_len <= 287, which is within the allocated 288-byte buffer. The null pointer check on p_msg prevents null dereference, and the conditional `if (p_msg->vendor_len && p_msg->p_vendor_data)` guards the memcpy from executing with zero length or null source.

Security invariants:
- The buffer allocation size must be strictly greater than the sum of minimum command length and vendor data length: enforced by `AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)` before calling osi_malloc
- The memcpy length must not cause writes beyond the allocated buffer: enforced by the pre-allocation size check which bounds vendor_len to at most 258, ensuring total writes (29 + vendor_len) fit within the 288-byte buffer
- The input pointer p_msg must be non-NULL before dereferencing: enforced by `if (!p_msg) { return NULL; }` at function entry
- The memcpy source pointer must be non-NULL when vendor_len is non-zero: enforced by the condition `if (p_msg->vendor_len && p_msg->p_vendor_data)` before the memcpy call
- The BT_HDR structure's flexible array member must accommodate all written data: enforced by the same size check that bounds total writes to within the allocated buffer

Invariant verification:
- Buffer size validation before memcpy: holds=true. Evidence: The condition `AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)` evaluates to `288 > (29 + vendor_len)`, which ensures vendor_len <= 258. The maximum write position is 29 + 258 = 287, within the 288-byte buffer.
- Null pointer dereference prevention for input parameter: holds=true. Evidence: The guard `if (!p_msg) { return NULL; }` at line 11 returns early if p_msg is NULL, preventing any subsequent dereference.
- Null source pointer protection for memcpy: holds=true. Evidence: The condition `if (p_msg->vendor_len && p_msg->p_vendor_data)` ensures memcpy is only called when both the length is non-zero and the source pointer is non-NULL.
- Correct payload length calculation: holds=true. Evidence: After writing 6 header bytes and vendor_len bytes, p_data points to offset 29 + vendor_len. The expression `(p_data + p_msg->vendor_len - (UINT8 *)(p_cmd + 1) - p_cmd->offset)` simplifies to 6 + vendor_len, correctly representing the payload size.

Residual risk notes: The function relies on osi_malloc returning a properly sized buffer when the allocation succeeds. If osi_malloc were to return a buffer smaller than requested (which would be a bug in the allocator), the bounds checks here would not catch it. Additionally, the function does not validate that p_msg->vendor_len does not exceed UINT16_MAX, though this is implicitly bounded by the buffer size check. The AVRC_META_CMD_BUF_SIZE path uses BT_SMALL_BUFFER_SIZE whose value is not visible in the provided context, so that path's safety depends on BT_SMALL_BUFFER_SIZE being sufficiently large.

```json
{
  "vulnerable": false,
  "cwe": null,
  "severity": "NONE",
  "reviewed_region": {
    "file_path": "components/bt/host/bluedroid/stack/avrc/avrc_opt.c",
    "symbol": "avrc_vendor_msg",
    "code": "static BT_HDR   *avrc_vendor_msg(tAVRC_MSG_VENDOR *p_msg)\n{\n    BT_HDR  *p_cmd = NULL;\n    UINT8   *p_data;\n\n/*\n  A vendor dependent command consists of at least of:\n  - A BT_HDR, plus\n  - AVCT_MSG_OFFSET, plus\n  - 3 bytes for ctype, subunit_type and op_vendor, plus\n  - 3 bytes for company_id\n*/\n#define AVRC_MIN_VENDOR_CMD_LEN (BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE)\n\n    if (!p_msg) {\n        return NULL;\n    }\n\n#if AVRC_METADATA_INCLUDED == TRUE\n    if ((AVRC_META_CMD_BUF_SIZE > AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len) &&\n        ((p_cmd = (BT_HDR *) osi_malloc(AVRC_META_CMD_BUF_SIZE)) != NULL))\n#else\n    if ((AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)) &&\n        (p_cmd = (BT_HDR *) osi_malloc(AVRC_CMD_BUF_SIZE)) != NULL)\n#endif\n    {\n        p_cmd->offset   = AVCT_MSG_OFFSET;\n        p_data          = (UINT8 *)(p_cmd + 1) + p_cmd->offset;\n        *p_data++       = (p_msg->hdr.ctype & AVRC_CTYPE_MASK);\n        *p_data++       = (p_msg->hdr.subunit_type << AVRC_SUBTYPE_SHIFT) | p_msg->hdr.subunit_id;\n        *p_data++       = AVRC_OP_VENDOR;\n        AVRC_CO_ID_TO_BE_STREAM(p_data, p_msg->company_id);\n        if (p_msg->vendor_len && p_msg->p_vendor_data) {\n            memcpy(p_data, p_msg->p_vendor_data, p_msg->vendor_len);\n        }\n        p_cmd->len  = (UINT16) (p_data + p_msg->vendor_len - (UINT8 *)(p_cmd + 1) - p_cmd->offset);\n        p_cmd->layer_specific   = AVCT_DATA_CTRL;\n    }\n    return p_cmd;\n}"
  },
  "vulnerable_region": null,
  "root_cause": null,
  "exploitability": null,
  "attack_preconditions": [],
  "impact": "No concrete vulnerability impact is demonstrated in the supplied code and context.",
  "non_vulnerable_reason": "The memcpy operation is properly bounded. The pre-allocation check `AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)` ensures that the buffer size (288 bytes) strictly exceeds the total data that will be written (AVRC_MIN_VENDOR_CMD_LEN + vendor_len = 29 + vendor_len). Since AVRC_MIN_VENDOR_CMD_LEN = BT_HDR_SIZE + AVCT_MSG_OFFSET + AVRC_VENDOR_HDR_SIZE = 8 + 15 + 6 = 29, the check guarantees vendor_len <= 258. The write position starts at offset 23 (BT_HDR_SIZE + AVCT_MSG_OFFSET = 8 + 15), and after writing 6 header bytes, memcpy writes vendor_len bytes ending at position 29 + vendor_len <= 287, which is within the allocated 288-byte buffer. The null pointer check on p_msg prevents null dereference, and the conditional `if (p_msg->vendor_len && p_msg->p_vendor_data)` guards the memcpy from executing with zero length or null source.",
  "security_invariants": [
    "The buffer allocation size must be strictly greater than the sum of minimum command length and vendor data length: enforced by `AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)` before calling osi_malloc",
    "The memcpy length must not cause writes beyond the allocated buffer: enforced by the pre-allocation size check which bounds vendor_len to at most 258, ensuring total writes (29 + vendor_len) fit within the 288-byte buffer",
    "The input pointer p_msg must be non-NULL before dereferencing: enforced by `if (!p_msg) { return NULL; }` at function entry",
    "The memcpy source pointer must be non-NULL when vendor_len is non-zero: enforced by the condition `if (p_msg->vendor_len && p_msg->p_vendor_data)` before the memcpy call",
    "The BT_HDR structure's flexible array member must accommodate all written data: enforced by the same size check that bounds total writes to within the allocated buffer"
  ],
  "invariant_verification": [
    {
      "property": "Buffer size validation before memcpy",
      "holds": true,
      "evidence": "The condition `AVRC_CMD_BUF_SIZE > (AVRC_MIN_VENDOR_CMD_LEN + p_msg->vendor_len)` evaluates to `288 > (29 + vendor_len)`, which ensures vendor_len <= 258. The maximum write position is 29 + 258 = 287, within the 288-byte buffer."
    },
    {
      "property": "Null pointer dereference prevention for input parameter",
      "holds": true,
      "evidence": "The guard `if (!p_msg) { return NULL; }` at line 11 returns early if p_msg is NULL, preventing any subsequent dereference."
    },
    {
      "property": "Null source pointer protection for memcpy",
      "holds": true,
      "evidence": "The condition `if (p_msg->vendor_len && p_msg->p_vendor_data)` ensures memcpy is only called when both the length is non-zero and the source pointer is non-NULL."
    },
    {
      "property": "Correct payload length calculation",
      "holds": true,
      "evidence": "After writing 6 header bytes and vendor_len bytes, p_data points to offset 29 + vendor_len. The expression `(p_data + p_msg->vendor_len - (UINT8 *)(p_cmd + 1) - p_cmd->offset)` simplifies to 6 + vendor_len, correctly representing the payload size."
    }
  ],
  "residual_risk_notes": "The function relies on osi_malloc returning a properly sized buffer when the allocation succeeds. If osi_malloc were to return a buffer smaller than requested (which would be a bug in the allocator), the bounds checks here would not catch it. Additionally, the function does not validate that p_msg->vendor_len does not exceed UINT16_MAX, though this is implicitly bounded by the buffer size check. The AVRC_META_CMD_BUF_SIZE path uses BT_SMALL_BUFFER_SIZE whose value is not visible in the provided context, so that path's safety depends on BT_SMALL_BUFFER_SIZE being sufficiently large.",
  "call_path": [
    "AVRC_VendorCmd",
    "avrc_vendor_msg",
    "osi_malloc",
    "memcpy"
  ]
}
```


System:
You are a senior application security auditor reviewing code for potential vulnerabilities.

You will be given a candidate function, repository and file metadata, a reduced call path, and resolved cross-file context such as helper functions, structs, macros, constants, and related definitions. Use the supplied context actively, but stay strictly grounded in what is actually shown.

Work from the supplied code and surrounding context as the primary evidence. Do not assume a bug exists just because the sample was retrieved.

Analyze both control flow and data flow. Trace the relevant execution paths and track security-relevant data through inputs, transformations, conditions, helper calls, outputs, and side effects.

Reason about attacker influence, identify relevant guards or missing guards, and explain exploitability or non-exploitability before reaching a verdict.

When the code is vulnerable, explain the vulnerable behavior, root cause, realistic attack preconditions, likely impact, and the most precise vulnerability classification justified by the supplied code.

When the code is not vulnerable, explain the concrete invariants and guards that prevent the suspected issue, and note any residual uncertainty that remains from the supplied context.

Keep the analysis concrete and code-grounded. Separate observed evidence from inferred conclusion.

Audit the following candidate code to determine whether it presents a real vulnerability.
This sample may come from a noisy retrieval or transformation stage, so reason from the supplied code and context rather than assuming the label.

Repository: github.com/TeamNewPipe/NewPipe
Language: Kotlin
File: app/src/main/kotlin/org/schabi/newpipe/settings/ContentSettingsManager.kt
Function: loadSharedPreferences

Call path: BackupRestoreSettingsFragment.requestImportPathResult (app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java) → BackupRestoreSettingsFragment.importDatabase (app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java) → ContentSettingsManager.loadSharedPreferences (app/src/main/kotlin/org/schabi/newpipe/settings/ContentSettingsManager.kt) → ObjectInputStream.readObject (java.io.ObjectInputStream)

### Primary Function

```kotlin
fun loadSerializedPrefs(zipFile: StoredFileHelper, preferences: SharedPreferences) {
        ZipHelper.extractFileFromZip(zipFile, BackupFileLocator.FILE_NAME_SERIALIZED_PREFS) {
            PreferencesObjectInputStream(it).use { input ->
                @Suppress("UNCHECKED_CAST")
                val entries = input.readObject() as Map<String, *>

                val editor = preferences.edit()
                editor.clear()

                for ((key, value) in entries) {
                    when (value) {
                        is Boolean -> editor.putBoolean(key, value)
                        is Float -> editor.putFloat(key, value)
                        is Int -> editor.putInt(key, value)
                        is Long -> editor.putLong(key, value)
                        is String -> editor.putString(key, value)
                        is Set<*> -> {
                            @Suppress("UNCHECKED_CAST")
                            editor.putStringSet(key, value as Set<String>?)
                        }
                    }
                }

                if (!editor.commit()) {
                    throw IOException("Unable to commit loadSerializedPrefs")
                }
            }
        }.let { fileExists ->
            if (!fileExists) {
                throw FileNotFoundException(BackupFileLocator.FILE_NAME_SERIALIZED_PREFS)
            }
        }
    }
```

### Cross-File Context

[ImportExportManager — class — app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt:18]
class ImportExportManager(private val fileLocator: BackupFileLocator)

[loadSerializedPrefs — function — app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt:108-146]
```kotlin
fun loadSerializedPrefs(zipFile: StoredFileHelper, preferences: SharedPreferences) {
        ZipHelper.extractFileFromZip(zipFile, BackupFileLocator.FILE_NAME_SERIALIZED_PREFS) {
            PreferencesObjectInputStream(it).use { input ->
                @Suppress("UNCHECKED_CAST")
                val entries = input.readObject() as Map<String, *>
                val editor = preferences.edit()
                editor.clear()
                for ((key, value) in entries) {
                    when (value) {
                        is Boolean -> editor.putBoolean(key, value)
                        is Float -> editor.putFloat(key, value)
                        is Int -> editor.putInt(key, value)
                        is Long -> editor.putLong(key, value)
                        is String -> editor.putString(key, value)
                        is Set<*> -> { @Suppress("UNCHECKED_CAST") editor.putStringSet(key, value as Set<String>?) }
                    }
                }
                if (!editor.commit()) { throw IOException("Unable to commit loadSerializedPrefs") }
            }
        }.let { fileExists ->
            if (!fileExists) { throw FileNotFoundException(BackupFileLocator.FILE_NAME_SERIALIZED_PREFS) }
        }
    }
```

[loadJsonPrefs — function — app/src/main/java/org/schabi/newpipe/settings/export/ImportExportManager.kt:152-185]
```kotlin
fun loadJsonPrefs(zipFile: StoredFileHelper, preferences: SharedPreferences) {
        ZipHelper.extractFileFromZip(zipFile, BackupFileLocator.FILE_NAME_JSON_PREFS) {
            val jsonObject = JsonParser.`object`().from(it)
            val editor = preferences.edit()
            editor.clear()
            for ((key, value) in jsonObject) {
                when (value) {
                    is Boolean -> editor.putBoolean(key, value)
                    is Float -> editor.putFloat(key, value)
                    is Int -> editor.putInt(key, value)
                    is Long -> editor.putLong(key, value)
                    is String -> editor.putString(key, value)
                    is JsonArray -> { editor.putStringSet(key, value.mapNotNull { e -> e as? String }.toSet()) }
                }
            }
            if (!editor.commit()) { throw IOException("Unable to commit loadJsonPrefs") }
        }.let { fileExists ->
            if (!fileExists) { throw FileNotFoundException(BackupFileLocator.FILE_NAME_JSON_PREFS) }
        }
    }
```

[PreferencesObjectInputStream — class — app/src/main/java/org/schabi/newpipe/settings/export/PreferencesObjectInputStream.kt:21-51]
class PreferencesObjectInputStream(stream: InputStream) : ObjectInputStream(stream) { @Throws(ClassNotFoundException::class, IOException::class) override fun resolveClass(desc: ObjectStreamClass): Class<*> { if (desc.name in CLASS_WHITELIST) { return super.resolveClass(desc) } else { throw ClassNotFoundException("Class not allowed: $desc.name") } } companion object { private val CLASS_WHITELIST = setOf<String>( "java.lang.Boolean", "java.lang.Byte", "java.lang.Character", "java.lang.Short", "java.lang.Integer", "java.lang.Long", "java.lang.Float", "java.lang.Double", "java.lang.Void", "java.util.HashMap", "java.util.HashSet" ) } }

[BackupFileLocator — class — app/src/main/java/org/schabi/newpipe/settings/export/BackupFileLocator.kt:10-25]
class BackupFileLocator(context: Context) { companion object { const val FILE_NAME_DB = "newpipe.db" const val FILE_NAME_SERIALIZED_PREFS = "newpipe.settings" const val FILE_NAME_JSON_PREFS = "preferences.json" } val db: Path = context.getDatabasePath(FILE_NAME_DB).toPath() val dbJournal: Path = db.resolveSibling("$FILE_NAME_DB-journal") val dbShm: Path = db.resolveSibling("$FILE_NAME_DB-shm") val dbWal: Path = db.resolveSibling("$FILE_NAME_DB-wal") }

[BackupRestoreSettingsFragment — class — app/src/main/java/org/schabi/newpipe/settings/BackupRestoreSettingsFragment.java:44]
public class BackupRestoreSettingsFragment extends BasePreferenceFragment

Provide the final verdict as strict JSON.

Return only one JSON object and no surrounding prose.

Return one JSON object with these keys:
- vulnerable
- cwe
- severity
- reviewed_region
- vulnerable_region
- root_cause
- exploitability
- attack_preconditions
- impact
- non_vulnerable_reason
- security_invariants
- invariant_verification
- residual_risk_notes
- call_path

Control flow: The function loadSerializedPrefs extracts a serialized preferences file from a zip archive via ZipHelper.extractFileFromZip. It then opens a PreferencesObjectInputStream on the extracted file stream within a use block for automatic resource cleanup. The stream reads a Map<String, *>, clears the SharedPreferences editor, iterates over the map entries applying a when-expression to dispatch each value to the appropriate SharedPreferences editor method (putBoolean, putFloat, putInt, putLong, putString, or putStringSet). After committing the editor, control flows to a let block that checks if the file existed in the zip, throwing FileNotFoundException if not. Exceptions during deserialization or editor commit propagate out.

Data flow: Input: a StoredFileHelper containing a zip file and a SharedPreferences instance. The serialized preferences file is extracted from the zip, then deserialized via PreferencesObjectInputStream which overrides resolveClass to enforce a CLASS_WHITELIST. The deserialized object is cast to Map<String, *> and each key-value pair is type-checked via a when-expression before being written to the SharedPreferences editor. Only Boolean, Float, Int, Long, String, and Set<*> types are processed. Output: the SharedPreferences is updated with the deserialized values, or an IOException/FileNotFoundException is thrown on failure.

Verdict: Not Vulnerable

Reviewed region:
[app/src/main/kotlin/org/schabi/newpipe/settings/ContentSettingsManager.kt]
```kotlin
fun loadSerializedPrefs(zipFile: StoredFileHelper, preferences: SharedPreferences) {
        ZipHelper.extractFileFromZip(zipFile, BackupFileLocator.FILE_NAME_SERIALIZED_PREFS) {
            PreferencesObjectInputStream(it).use { input ->
                @Suppress("UNCHECKED_CAST")
                val entries = input.readObject() as Map<String, *>

                val editor = preferences.edit()
                editor.clear()

                for ((key, value) in entries) {
                    when (value) {
                        is Boolean -> editor.putBoolean(key, value)
                        is Float -> editor.putFloat(key, value)
                        is Int -> editor.putInt(key, value)
                        is Long -> editor.putLong(key, value)
                        is String -> editor.putString(key, value)
                        is Set<*> -> {
                            @Suppress("UNCHECKED_CAST")
                            editor.putStringSet(key, value as Set<String>?)
                        }
                    }
                }

                if (!editor.commit()) {
                    throw IOException("Unable to commit loadSerializedPrefs")
                }
            }
        }.let { fileExists ->
            if (!fileExists) {
                throw FileNotFoundException(BackupFileLocator.FILE_NAME_SERIALIZED_PREFS)
            }
        }
    }
```

Why it is not vulnerable: The deserialization is performed through PreferencesObjectInputStream which overrides resolveClass to enforce a strict CLASS_WHITELIST containing only java.lang.Boolean, java.lang.Byte, java.lang.Character, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Float, java.lang.Double, java.lang.Void, java.util.HashMap, and java.util.HashSet. These classes do not have dangerous constructors, finalize methods, or serialization callbacks that could lead to arbitrary code execution. Additionally, after deserialization, the values undergo type checking via a when-expression that only accepts the expected primitive types and String Set, preventing unexpected object types from being stored.

Security invariants:
- Deserialization must not resolve classes outside a trusted whitelist - enforced by PreferencesObjectInputStream.resolveClass which throws ClassNotFoundException for any class name not in CLASS_WHITELIST
- Deserialized values must be type-checked before storage - enforced by the when-expression that only processes Boolean, Float, Int, Long, String, and Set<*> types, ignoring or failing on unexpected types
- Only HashMap and HashSet are allowed as container classes - enforced by the CLASS_WHITELIST which excludes collection types with dangerous serialization behaviors

Invariant verification:
- Class resolution whitelist prevents arbitrary class instantiation during deserialization: holds=true. Evidence: PreferencesObjectInputStream.resolveClass checks if desc.name in CLASS_WHITELIST before calling super.resolveClass, and throws ClassNotFoundException otherwise. The whitelist contains only safe JDK core types: java.lang.Boolean, java.lang.Byte, java.lang.Character, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Float, java.lang.Double, java.lang.Void, java.util.HashMap, java.util.HashSet
- Deserialized values are validated before being stored in SharedPreferences: holds=true. Evidence: The when-expression on each value in the map explicitly checks for is Boolean, is Float, is Int, is Long, is String, and is Set<*> before calling the corresponding editor.put method
- Container classes (HashMap, HashSet) in the whitelist do not have exploitable serialization hooks: holds=true. Evidence: java.util.HashMap and java.util.HashSet have readObject methods that only restore their own internal state and delegate element deserialization through the ObjectInputStream's resolveClass mechanism, which continues to enforce the whitelist

Residual risk notes: The @Suppress("UNCHECKED_CAST") annotations on the Map cast and the Set<String> cast mean type safety relies on runtime behavior rather than compile-time guarantees. If a malicious serialized stream somehow contained unexpected nested types, the when-expression would throw a ClassCastException at runtime rather than storing harmful data. The security posture depends on the completeness of the CLASS_WHITELIST and the correctness of PreferencesObjectInputStream.resolveClass being the sole enforcement point.

Control flow: The function extracts a file from a zip archive via `ZipHelper.extractFileFromZip`, then deserializes it using `PreferencesObjectInputStream`. The deserialized object is cast to `Map<String, *>` and iterated. Each entry's value is type-checked via a `when` expression and written to a `SharedPreferences.Editor` using the appropriate `put*` method. The editor is committed, and if the file does not exist in the archive, a `FileNotFoundException` is thrown. All I/O resources are managed via `.use` for automatic cleanup.

Data flow: Input `zipFile` is processed by `ZipHelper.extractFileFromZip` which yields an InputStream. The stream is wrapped in `PreferencesObjectInputStream` which extends `ObjectInputStream`. `readObject()` deserializes the stream into a `Map<String, *>`. Each key-value pair is validated by type using a `when` expression (Boolean, Float, Int, Long, String, Set) and written to the `SharedPreferences` editor. The editor is committed to persist changes.

Verdict: Not Vulnerable

Reviewed region:
[app/src/main/kotlin/org/schabi/newpipe/settings/ContentSettingsManager.kt]
```kotlin
fun loadSerializedPrefs(zipFile: StoredFileHelper, preferences: SharedPreferences) {
        ZipHelper.extractFileFromZip(zipFile, BackupFileLocator.FILE_NAME_SERIALIZED_PREFS) {
            PreferencesObjectInputStream(it).use { input ->
                @Suppress("UNCHECKED_CAST")
                val entries = input.readObject() as Map<String, *>

                val editor = preferences.edit()
                editor.clear()

                for ((key, value) in entries) {
                    when (value) {
                        is Boolean -> editor.putBoolean(key, value)
                        is Float -> editor.putFloat(key, value)
                        is Int -> editor.putInt(key, value)
                        is Long -> editor.putLong(key, value)
                        is String -> editor.putString(key, value)
                        is Set<*> -> {
                            @Suppress("UNCHECKED_CAST")
                            editor.putStringSet(key, value as Set<String>?)
                        }
                    }
                }

                if (!editor.commit()) {
                    throw IOException("Unable to commit loadSerializedPrefs")
                }
            }
        }.let { fileExists ->
            if (!fileExists) {
                throw FileNotFoundException(BackupFileLocator.FILE_NAME_SERIALIZED_PREFS)
            }
        }
    }
```

Why it is not vulnerable: The deserialization operation uses `PreferencesObjectInputStream`, which overrides `resolveClass(ObjectStreamClass)` to enforce a strict class whitelist. The `CLASS_WHITELIST` contains only safe classes (`java.lang.Boolean`, `java.lang.Integer`, `java.lang.Long`, `java.lang.Float`, `java.lang.Double`, `java.lang.String`, `java.util.HashMap`, `java.util.HashSet`). Any attempt to deserialize a class outside this whitelist throws `ClassNotFoundException`, preventing arbitrary class instantiation. Additionally, the `when` expression further restricts accepted value types to those compatible with `SharedPreferences`, providing defense-in-depth.

Security invariants:
- Deserialization must restrict instantiable classes to a safe whitelist — enforced by `PreferencesObjectInputStream.resolveClass()` checking `desc.name in CLASS_WHITELIST` before calling `super.resolveClass(desc)`
- Deserialized values must be type-checked before use — enforced by the `when` expression that only accepts `Boolean`, `Float`, `Int`, `Long`, `String`, and `Set<*>`
- I/O resources must be properly closed — enforced by `.use` on `PreferencesObjectInputStream`
- File existence must be verified — enforced by the `.let` block checking `fileExists` and throwing `FileNotFoundException` if false

Invariant verification:
- Class whitelist enforcement during deserialization: holds=true. Evidence: `PreferencesObjectInputStream` overrides `resolveClass(desc: ObjectStreamClass)` and checks `if (desc.name in CLASS_WHITELIST)` before resolving. The `CLASS_WHITELIST` contains only safe classes: `java.lang.Boolean`, `java.lang.Byte`, `java.lang.Character`, `java.lang.Short`, `java.lang.Integer`, `java.lang.Long`, `java.lang.Float`, `java.lang.Double`, `java.lang.Void`, `java.util.HashMap`, `java.util.HashSet`. Any other class triggers `throw ClassNotFoundException("Class not allowed: $desc.name")`.
- Type validation of deserialized values: holds=true. Evidence: The `when` expression checks `value` against `is Boolean`, `is Float`, `is Int`, `is Long`, `is String`, `is Set<*>` before writing to SharedPreferences. Only matching types are processed; no default branch exists that would accept arbitrary types.
- Resource cleanup: holds=true. Evidence: `PreferencesObjectInputStream(it).use { input -> ... }` ensures the stream is closed even if an exception occurs during deserialization.
- File existence validation: holds=true. Evidence: The `.let { fileExists -> if (!fileExists) { throw FileNotFoundException(...) } }` block verifies the file was found in the archive before proceeding.

Residual risk: The `CLASS_WHITELIST` includes `java.util.HashMap` and `java.util.HashSet`, which are necessary for deserializing the expected Map structure. These classes are standard JDK collections with no known deserialization gadgets. The whitelist is hardcoded and not configurable at runtime, reducing the risk of whitelist manipulation. The defense-in-depth provided by the `when` type check further limits the impact of any theoretical edge cases in the whitelisted classes.

```json
{
  "vulnerable": false,
  "cwe": null,
  "severity": "NONE",
  "reviewed_region": {
    "file_path": "app/src/main/kotlin/org/schabi/newpipe/settings/ContentSettingsManager.kt",
    "symbol": "loadSharedPreferences",
    "code": "fun loadSerializedPrefs(zipFile: StoredFileHelper, preferences: SharedPreferences) {\n        ZipHelper.extractFileFromZip(zipFile, BackupFileLocator.FILE_NAME_SERIALIZED_PREFS) {\n            PreferencesObjectInputStream(it).use { input ->\n                @Suppress(\"UNCHECKED_CAST\")\n                val entries = input.readObject() as Map<String, *>\n\n                val editor = preferences.edit()\n                editor.clear()\n\n                for ((key, value) in entries) {\n                    when (value) {\n                        is Boolean -> editor.putBoolean(key, value)\n                        is Float -> editor.putFloat(key, value)\n                        is Int -> editor.putInt(key, value)\n                        is Long -> editor.putLong(key, value)\n                        is String -> editor.putString(key, value)\n                        is Set<*> -> {\n                            @Suppress(\"UNCHECKED_CAST\")\n                            editor.putStringSet(key, value as Set<String>?)\n                        }\n                    }\n                }\n\n                if (!editor.commit()) {\n                    throw IOException(\"Unable to commit loadSerializedPrefs\")\n                }\n            }\n        }.let { fileExists ->\n            if (!fileExists) {\n                throw FileNotFoundException(BackupFileLocator.FILE_NAME_SERIALIZED_PREFS)\n            }\n        }\n    }"
  },
  "vulnerable_region": null,
  "root_cause": null,
  "exploitability": null,
  "attack_preconditions": [],
  "impact": "No concrete vulnerability impact is demonstrated in the supplied code and context.",
  "non_vulnerable_reason": "The deserialization operation uses `PreferencesObjectInputStream`, which overrides `resolveClass(ObjectStreamClass)` to enforce a strict class whitelist. The `CLASS_WHITELIST` contains only safe classes (`java.lang.Boolean`, `java.lang.Integer`, `java.lang.Long`, `java.lang.Float`, `java.lang.Double`, `java.lang.String`, `java.util.HashMap`, `java.util.HashSet`). Any attempt to deserialize a class outside this whitelist throws `ClassNotFoundException`, preventing arbitrary class instantiation. Additionally, the `when` expression further restricts accepted value types to those compatible with `SharedPreferences`, providing defense-in-depth.",
  "security_invariants": [
    "Deserialization must restrict instantiable classes to a safe whitelist — enforced by `PreferencesObjectInputStream.resolveClass()` checking `desc.name in CLASS_WHITELIST` before calling `super.resolveClass(desc)`",
    "Deserialized values must be type-checked before use — enforced by the `when` expression that only accepts `Boolean`, `Float`, `Int`, `Long`, `String`, and `Set<*>`",
    "I/O resources must be properly closed — enforced by `.use` on `PreferencesObjectInputStream`",
    "File existence must be verified — enforced by the `.let` block checking `fileExists` and throwing `FileNotFoundException` if false"
  ],
  "invariant_verification": [
    {
      "property": "Class whitelist enforcement during deserialization",
      "holds": true,
      "evidence": "`PreferencesObjectInputStream` overrides `resolveClass(desc: ObjectStreamClass)` and checks `if (desc.name in CLASS_WHITELIST)` before resolving. The `CLASS_WHITELIST` contains only safe classes: `java.lang.Boolean`, `java.lang.Byte`, `java.lang.Character`, `java.lang.Short`, `java.lang.Integer`, `java.lang.Long`, `java.lang.Float`, `java.lang.Double`, `java.lang.Void`, `java.util.HashMap`, `java.util.HashSet`. Any other class triggers `throw ClassNotFoundException(\"Class not allowed: $desc.name\")`."
    },
    {
      "property": "Type validation of deserialized values",
      "holds": true,
      "evidence": "The `when` expression checks `value` against `is Boolean`, `is Float`, `is Int`, `is Long`, `is String`, `is Set<*>` before writing to SharedPreferences. Only matching types are processed; no default branch exists that would accept arbitrary types."
    },
    {
      "property": "Resource cleanup",
      "holds": true,
      "evidence": "`PreferencesObjectInputStream(it).use { input -> ... }` ensures the stream is closed even if an exception occurs during deserialization."
    },
    {
      "property": "File existence validation",
      "holds": true,
      "evidence": "The `.let { fileExists -> if (!fileExists) { throw FileNotFoundException(...) } }` block verifies the file was found in the archive before proceeding."
    }
  ],
  "residual_risk_notes": "The `CLASS_WHITELIST` includes `java.util.HashMap` and `java.util.HashSet`, which are necessary for deserializing the expected Map structure. These classes are standard JDK collections with no known deserialization gadgets. The whitelist is hardcoded and not configurable at runtime, reducing the risk of whitelist manipulation. The defense-in-depth provided by the `when` type check further limits the impact of any theoretical edge cases in the whitelisted classes.",
  "call_path": [
    "BackupRestoreSettingsFragment.requestImportPathResult",
    "BackupRestoreSettingsFragment.importDatabase",
    "ContentSettingsManager.loadSharedPreferences",
    "ObjectInputStream.readObject"
  ]
}
```


Audit the following candidate code to determine whether it presents a real vulnerability.
This sample may come from a noisy retrieval or transformation stage, so reason from the supplied code and context rather than assuming the label.

Repository: github.com/chamilo/chamilo-lms
Language: PHP
File: main/inc/lib/formvalidator/Element/HtmlEditor.php
Function: getValue

Call path: HtmlEditor::toHtml (main/inc/lib/formvalidator/Element/HtmlEditor.php) → HtmlEditor::getValue (main/inc/lib/formvalidator/Element/HtmlEditor.php) → HtmlEditor::getFrozenHtml (main/inc/lib/formvalidator/Element/HtmlEditor.php) → Security::remove_XSS (main/inc/lib/security.lib.php) → RemoveOnAttributes::filter (src/CoreBundle/Component/HTMLPurifier/Filter/RemoveOnAttributes.php)

### Primary Function

```php
public function getFrozenHtml()
    {
        return Security::remove_XSS($this->getValue());
    }
```

### Cross-File Context

[HtmlEditor — class — main/inc/lib/formvalidator/Element/HtmlEditor.php]
class HtmlEditor extends HTML_QuickForm_textarea

[getFrozenHtml — method — main/inc/lib/formvalidator/Element/HtmlEditor.php:82-86]
public function getFrozenHtml() { return Security::remove_XSS($this->getValue()); }

[Security::remove_XSS — sink — main/inc/lib/security.lib.php]
```php
function remove_XSS($html)
```

Control flow: User submits HTML content via form using HtmlEditor -> content stored in $this->_value -> when rendered, getValue() returns raw value -> getFrozenHtml() calls Security::remove_XSS() -> XSS filter may not catch all on* vectors

Data flow: User-controlled HTML input flows from $_POST/$_REQUEST through HtmlEditor constructor into $this->_value, then through getValue() (which had no filtering pre-fix) to rendering methods getFrozenHtml()/buildEditor() where Security::remove_XSS() is applied.

Verdict: Vulnerable
Vulnerable region:
[main/inc/lib/formvalidator/Element/HtmlEditor.php]
```php
/**
     * Returns the html area content in HTML.
     *
     * @return string
     */
```
CWE: CWE-79
Severity: HIGH

Root cause: The HtmlEditor class's getValue() method (added in fix) returned the raw $this->_value without removing dangerous HTML event handler attributes (on* attributes like onclick, onerror, onload, etc.). When this value was rendered via getFrozenHtml() -> Security::remove_XSS(), only some XSS vectors were blocked but on* attributes could bypass the protection. An attacker could craft HTML content with malicious on* attributes that would be stored and rendered without proper sanitization.

Exploitability: An authenticated user with access to HTML editor fields can inject malicious HTML containing on* event handler attributes (onclick, onerror, onload, etc.) that bypass existing XSS filters and execute arbitrary JavaScript when content is viewed by other users.

Attack preconditions:
- Attacker must have access to a form/area that uses HtmlEditor component
- Attacker must be able to submit HTML content with malicious on* attributes
- Content must be stored and later rendered without additional sanitization

Likely impact: An authenticated user with access to HTML editor fields can inject malicious HTML containing on* event handler attributes (onclick, onerror, onload, etc.) that bypass existing XSS filters and execute arbitrary JavaScript when content is viewed by other users.

HtmlEditor::toHtml HtmlEditor::getValue HtmlEditor::getFrozenHtml Security::remove_XSS RemoveOnAttributes::filter

Control flow: The getFrozenHtml() method has a single linear execution path: it retrieves the current value via $this->getValue(), passes it through Security::remove_XSS() for sanitization, and returns the sanitized result. There are no conditional branches, error handling paths, or alternative return statements.

Data flow: User-supplied HTML content is stored internally and retrieved via getValue(). This value flows directly into Security::remove_XSS(), which serves as the sanitization sink. Based on the call path, Security::remove_XSS() invokes HTMLPurifier with the RemoveOnAttributes filter to strip event handler attributes. The sanitized output is then returned for display in the frozen state.

Verdict: Not Vulnerable

Reviewed region:
[main/inc/lib/formvalidator/Element/HtmlEditor.php]
```php
public function getFrozenHtml()
    {
        return Security::remove_XSS($this->getValue());
    }
```

Why it is not vulnerable: The getFrozenHtml() method consistently passes all output through Security::remove_XSS() before returning it. The single return statement wraps the entire output path with the sanitization call, ensuring no raw value can bypass filtering. The call path confirms that Security::remove_XSS() leverages HTMLPurifier with RemoveOnAttributes filter, which provides structured HTML sanitization including removal of dangerous event handler attributes.

Security invariants:
- All values returned by getFrozenHtml() must pass through Security::remove_XSS() before output - enforced by the single return statement: return Security::remove_XSS($this->getValue());
- The sanitization function must neutralize dangerous HTML elements and attributes - enforced by the call path showing Security::remove_XSS() uses HTMLPurifier with RemoveOnAttributes filter
- No alternative code paths should return unsanitized data - enforced by the absence of conditional branches or early returns in the method body

Invariant verification:
- Output sanitization applied to all return paths: holds=true. Evidence: The method body contains only one return statement: return Security::remove_XSS($this->getValue()); which wraps the entire output in the sanitization call
- HTMLPurifier-based sanitization is invoked: holds=true. Evidence: The call path shows Security::remove_XSS() leads to RemoveOnAttributes::filter, indicating HTMLPurifier processing with event handler attribute removal
- No raw value bypasses sanitization: holds=true. Evidence: The method has no conditional logic, early returns, or alternative paths that could return $this->getValue() without passing it through Security::remove_XSS()

Residual risk notes: The full implementation of Security::remove_XSS() is not available in the supplied context, so the completeness of the HTMLPurifier configuration (e.g., whether it properly handles all dangerous elements, protocols, and CSS-based attacks) cannot be fully verified. Additionally, the behavior of getValue() is not shown, though it appears to be a standard retrieval method. The effectiveness of the sanitization ultimately depends on the HTMLPurifier configuration within Security::remove_XSS().


Audit the following candidate code to determine whether it presents a real vulnerability.
This sample may come from a noisy retrieval or transformation stage, so reason from the supplied code and context rather than assuming the label.

Repository: git.kernel.org/pub/scm/linux/kernel/git/stable/linux
Language: C
File: kernel/sched/fair.c
Function: reweight_eevdf

Call path: reweight_eevdf (kernel/sched/fair.c) → entity_eligible (kernel/sched/fair.c) → pick_eevdf (kernel/sched/fair.c) → update_entity_lag (kernel/sched/fair.c) → calc_delta_fair (kernel/sched/fair.c) → avg_vruntime (kernel/sched/fair.c)

### Primary Function

```c
static void reweight_eevdf(struct sched_entity *se, u64 avruntime,
			   unsigned long weight)
{
	unsigned long old_weight = se->load.weight;
	s64 vlag, vslice;

	/*
	 * VRUNTIME
	 * ========
	 *
	 * COROLLARY #1: The virtual runtime of the entity needs to be
	 * adjusted if re-weight at !0-lag point.
	 *
	 * Proof: For contradiction assume this is not true, so we can
	 * re-weight without changing vruntime at !0-lag point.
	 *
	 *             Weight	VRuntime   Avg-VRuntime
	 *     before    w          v            V
	 *      after    w'         v'           V'
	 *
	 * Since lag needs to be preserved through re-weight:
	 *
	 *	lag = (V - v)*w = (V'- v')*w', where v = v'
	 *	==>	V' = (V - v)*w/w' + v		(1)
	 *
	 * Let W be the total weight of the entities before reweight,
	 * since V' is the new weighted average of entities:
	 *
	 *	V' = (WV + w'v - wv) / (W + w' - w)	(2)
	 *
	 * by using (1) & (2) we obtain:
	 *
	 *	(WV + w'v - wv) / (W + w' - w) = (V - v)*w/w' + v
	 *	==> (WV-Wv+Wv+w'v-wv)/(W+w'-w) = (V - v)*w/w' + v
	 *	==> (WV - Wv)/(W + w' - w) + v = (V - v)*w/w' + v
	 *	==>	(V - v)*W/(W + w' - w) = (V - v)*w/w' (3)
	 *
	 * Since we are doing at !0-lag point which means V != v, we
	 * can simplify (3):
	 *
	 *	==>	W / (W + w' - w) = w / w'
	 *	==>	Ww' = Ww + ww' - ww
	 *	==>	W * (w' - w) = w * (w' - w)
	 *	==>	W = w	(re-weight indicates w' != w)
	 *
	 * So the cfs_rq contains only one entity, hence vruntime of
	 * the entity @v should always equal to the cfs_rq's weighted
	 * average vruntime @V, which means we will always re-weight
	 * at 0-lag point, thus breach assumption. Proof completed.
	 *
	 *
	 * COROLLARY #2: Re-weight does NOT affect weighted average
	 * vruntime of all the entities.
	 *
	 * Proof: According to corollary #1, Eq. (1) should be:
	 *
	 *	(V - v)*w = (V' - v')*w'
	 *	==>	    v' = V' - (V - v)*w/w'		(4)
	 *
	 * According to the weighted average formula, we have:
	 *
	 *	V' = (WV - wv + w'v') / (W - w + w')
	 *	   = (WV - wv + w'(V' - (V - v)w/w')) / (W - w + w')
	 *	   = (WV - wv + w'V' - Vw + wv) / (W - w + w')
	 *	   = (WV + w'V' - Vw) / (W - w + w')
	 *
	 *	==>	V'*(W - w + w') = WV + w'V' - Vw
	 *	==>	V' * (W - w) = (W - w) * V	(5)
	 *
	 * If the entity is the only one in the cfs_rq, then reweight
	 * always occurs at 0-lag point, so V won't change. Or else
	 * there are other entities, hence W != w, then Eq. (5) turns
	 * into V' = V. So V won't change in either case, proof done.
	 *
	 *
	 * So according to corollary #1 & #2, the effect of re-weight
	 * on vruntime should be:
	 *
	 *	v' = V' - (V - v) * w / w'		(4)
	 *	   = V  - (V - v) * w / w'
	 *	   = V  - vl * w / w'
	 *	   = V  - vl'
	 */
	if (avruntime != se->vruntime) {
		vlag = entity_lag(avruntime, se);
		vlag = div_s64(vlag * old_weight, weight);
		se->vruntime = avruntime - vlag;
	}

	/*
	 * DEADLINE
	 * ========
	 *
	 * When the weight changes, the virtual time slope changes and
	 * we should adjust the relative virtual deadline accordingly.
	 *
	 *	d' = v' + (d - v)*w/w'
	 *	   = V' - (V - v)*w/w' + (d - v)*w/w'
	 *	   = V  - (V - v)*w/w' + (d - v)*w/w'
	 *	   = V  + (d - V)*w/w'
	 */
	vslice = (s64)(se->deadline - avruntime);
	vslice = div_s64(vslice * old_weight, weight);
	se->deadline = avruntime + vslice;
}
```

### Cross-File Context

[entity_lag — function — kernel/sched/fair.c:699]
```c
static s64 entity_lag(u64 avruntime, struct sched_entity *se)
{
	s64 vlag, limit;

	vlag = avruntime - se->vruntime;
	limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se);

	return clamp(vlag, -limit, limit);
}
```

[update_entity_lag — helper — kernel/sched/fair.c:709]
```c
static void update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	SCHED_WARN_ON(!se->on_rq);

	se->vlag = entity_lag(avg_vruntime(cfs_rq), se);
}
```

[calc_delta_fair — helper — kernel/sched/fair.c:296]
```c
static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se)
{
	if (unlikely(se->load.weight != NICE_0_LOAD))
		delta = __calc_delta(delta, NICE_0_LOAD, &se->load);

	return delta;
}
```

[avg_vruntime — helper — kernel/sched/fair.c:665]
```c
u64 avg_vruntime(struct cfs_rq *cfs_rq)
{
	struct sched_entity *curr = cfs_rq->curr;
	s64 avg = cfs_rq->avg_vruntime;
	long load = cfs_rq->avg_load;

	if (curr && curr->on_rq) {
		unsigned long weight = scale_load_down(curr->load.weight);

		avg += entity_key(cfs_rq, curr) * weight;
		load += weight;
	}

	if (load) {
		/* sign flips effective floor / ceil */
		if (avg < 0)
			avg -= (load - 1);
		avg = div_s64(avg, load);
	}

	return cfs_rq->min_vruntime + avg;
}
```

[entity_eligible — callee — kernel/sched/fair.c:749]
```c
int entity_eligible(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
	return vruntime_eligible(cfs_rq, se->vruntime);
}
```

[pick_eevdf — callee — kernel/sched/fair.c:884]
```c
static struct sched_entity *pick_eevdf(struct cfs_rq *cfs_rq)
{
	struct rb_node *node = cfs_rq->tasks_timeline.rb_root.rb_node;
	struct sched_entity *se = __pick_first_entity(cfs_rq);
	struct sched_entity *curr = cfs_rq->curr;
	struct sched_entity *best = NULL;

	if (cfs_rq->nr_running == 1)
		return curr && curr->on_rq ? curr : se;

	if (curr && (!curr->on_rq || !entity_eligible(cfs_rq, curr)))
		curr = NULL;

	if (sched_feat(RUN_TO_PARITY) && curr && curr->vlag == curr->deadline)
		return curr;

	if (se && entity_eligible(cfs_rq, se)) {
		best = se;
		goto found;
	}

	while (node) {
		struct rb_node *left = node->rb_left;

		if (left && vruntime_eligible(cfs_rq,
					__node_2_se(left)->min_vruntime)) {
			node = left;
			continue;
		}

		se = __node_2_se(node);

		if (entity_eligible(cfs_rq, se)) {
			best = se;
			break;
		}

		node = node->rb_right;
	}
found:
	if (!best || (curr && entity_before(curr, best)))
		best = curr;

	return best;
}
```

[sched_entity — struct — include/linux/sched.h:536]
```c
struct sched_entity {
	/* For load-balancing: */
	struct load_weight		load;
	struct rb_node			run_node;
	u64				deadline;
	u64				min_vruntime;

	struct list_head		group_node;
	unsigned int			on_rq;

	u64				exec_start;
	u64			sum_exec_runtime;
	u64			prev_sum_exec_runtime;
	u64			vruntime;
	s64			vlag;
	u64			slice;

	u64			nr_migrations;

#ifdef CONFIG_FAIR_GROUP_SCHED
	int			depth;
	struct sched_entity	*parent;
	/* rq on which this entity is (to be) queued: */
	struct cfs_rq		*cfs_rq;
	/* rq "owned" by this entity/group: */
	struct cfs_rq		*my_q;
	/* cached value of my_q->h_nr_running */
	unsigned long		-runnable_weight;
#endif

#ifdef CONFIG_SMP
	/*
	 * Per entity load average tracking.
	 *
	 * Put into separate cache line so it does not
	 * collide with read-mostly values above.
	 */
	struct sched_avg	avg;
#endif
};
```

[clamp — macro — linux/kernel.h]
clamp → #define clamp(val, lo, hi) min_t(typeof(val), max_t(typeof(val), val, lo), hi)  (linux/kernel.h)

[TICK_NSEC — constant — linux/jiffies.h]
TICK_NSEC → (NSEC_PER_SEC / HZ)  (linux/jiffies.h)

Control flow: The reweight_eevdf function has two sequential branches: (1) VRUNTIME adjustment block that executes only when avruntime != se->vruntime, computing entity_lag, scaling by weight ratio via div_s64, and updating se->vruntime; (2) DEADLINE adjustment block that unconditionally computes vslice from se->deadline and avruntime, scales by weight ratio via div_s64, and updates se->deadline. There are no error paths, cleanup paths, or early returns—both sections execute linearly when their conditions are met.

Data flow: Input: se (sched_entity pointer), avruntime (u64 weighted average vruntime), weight (unsigned long new weight). Intermediate: old_weight extracted from se->load.weight. In the VRUNTIME branch, entity_lag(avruntime, se) returns a clamped lag value via entity_lag() which subtracts se->vruntime from avruntime and clamps to [-limit, limit] where limit is calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se). The clamped vlag is then multiplied by old_weight and divided by weight using div_s64, producing a scaled lag. se->vruntime is set to avruntime - scaled_vlag. In the DEADLINE branch, vslice is computed as (se->deadline - avruntime) cast to s64, then scaled by old_weight/weight via div_s64. se->deadline is set to avruntime + scaled_vslice. All output writes modify fields within se.

Verdict: Not Vulnerable

Reviewed region:
[kernel/sched/fair.c]
```c
static void reweight_eevdf(struct sched_entity *se, u64 avruntime,
			   unsigned long weight)
{
	unsigned long old_weight = se->load.weight;
	s64 vlag, vslice;

	/*
	 * VRUNTIME
	 * ========
	 *
	 * COROLLARY #1: The virtual runtime of the entity needs to be
	 * adjusted if re-weight at !0-lag point.
	 *
	 * Proof: For contradiction assume this is not true, so we can
	 * re-weight without changing vruntime at !0-lag point.
	 *
	 *             Weight	VRuntime   Avg-VRuntime
	 *     before    w          v            V
	 *      after    w'         v'           V'
	 *
	 * Since lag needs to be preserved through re-weight:
	 *
	 *	lag = (V - v)*w = (V'- v')*w', where v = v'
	 *	==>	V' = (V - v)*w/w' + v		(1)
	 *
	 * Let W be the total weight of the entities before reweight,
	 * since V' is the new weighted average of entities:
	 *
	 *	V' = (WV + w'v - wv) / (W + w' - w)	(2)
	 *
	 * by using (1) & (2) we obtain:
	 *
	 *	(WV + w'v - wv) / (W + w' - w) = (V - v)*w/w' + v
	 *	==> (WV-Wv+Wv+w'v-wv)/(W+w'-w) = (V - v)*w/w' + v
	 *	==> (WV - Wv)/(W + w' - w) + v = (V - v)*w/w' + v
	 *	==>	(V - v)*W/(W + w' - w) = (V - v)*w/w' (3)
	 *
	 * Since we are doing at !0-lag point which means V != v, we
	 * can simplify (3):
	 *
	 *	==>	W / (W + w' - w) = w / w'
	 *	==>	Ww' = Ww + ww' - ww
	 *	==>	W * (w' - w) = w * (w' - w)
	 *	==>	W = w	(re-weight indicates w' != w)
	 *
	 * So the cfs_rq contains only one entity, hence vruntime of
	 * the entity @v should always equal to the cfs_rq's weighted
	 * average vruntime @V, which means we will always re-weight
	 * at 0-lag point, thus breach assumption. Proof completed.
	 *
	 *
	 * COROLLARY #2: Re-weight does NOT affect weighted average
	 * vruntime of all the entities.
	 *
	 * Proof: According to corollary #1, Eq. (1) should be:
	 *
	 *	(V - v)*w = (V' - v')*w'
	 *	==>	    v' = V' - (V - v)*w/w'		(4)
	 *
	 * According to the weighted average formula, we have:
	 *
	 *	V' = (WV - wv + w'v') / (W - w + w')
	 *	   = (WV - wv + w'(V' - (V - v)w/w')) / (W - w + w')
	 *	   = (WV - wv + w'V' - Vw + wv) / (W - w + w')
	 *	   = (WV + w'V' - Vw) / (W - w + w')
	 *
	 *	==>	V'*(W - w + w') = WV + w'V' - Vw
	 *	==>	V' * (W - w) = (W - w) * V	(5)
	 *
	 * If the entity is the only one in the cfs_rq, then reweight
	 * always occurs at 0-lag point, so V won't change. Or else
	 * there are other entities, hence W != w, then Eq. (5) turns
	 * into V' = V. So V won't change in either case, proof done.
	 *
	 *
	 * So according to corollary #1 & #2, the effect of re-weight
	 * on vruntime should be:
	 *
	 *	v' = V' - (V - v) * w / w'		(4)
	 *	   = V  - (V - v) * w / w'
	 *	   = V  - vl * w / w'
	 *	   = V  - vl'
	 */
	if (avruntime != se->vruntime) {
		vlag = entity_lag(avruntime, se);
		vlag = div_s64(vlag * old_weight, weight);
		se->vruntime = avruntime - vlag;
	}

	/*
	 * DEADLINE
	 * ========
	 *
	 * When the weight changes, the virtual time slope changes and
	 * we should adjust the relative virtual deadline accordingly.
	 *
	 *	d' = v' + (d - v)*w/w'
	 *	   = V' - (V - v)*w/w' + (d - v)*w/w'
	 *	   = V  - (V - v)*w/w' + (d - v)*w/w'
	 *	   = V  + (d - V)*w/w'
	 */
	vslice = (s64)(se->deadline - avruntime);
	vslice = div_s64(vslice * old_weight, weight);
	se->deadline = avruntime + vslice;
}
```

Why it is not vulnerable: The critical operation vlag * old_weight in the VRUNTIME branch cannot overflow because vlag is clamped by entity_lag() to [-limit, limit] where limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se). Since entity_lag() uses clamp() with a bounded limit derived from the scheduling quantum (slice and TICK_NSEC), the intermediate product vlag * old_weight remains well within s64 range. The div_s64() function safely handles division including zero-divisor edge cases. Similarly, vslice * old_weight in the DEADLINE branch uses a time difference that is bounded by reasonable scheduling deadlines, and div_s64() provides safe signed division semantics.

Security invariants:
- vlag must be bounded before multiplication by old_weight; enforced by entity_lag() which returns clamp(vlag, -limit, limit) where limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se)
- Division by weight must not cause division-by-zero or undefined behavior; enforced by div_s64() which checks for zero divisor and returns a safe fallback value
- The signed arithmetic in vlag = div_s64(vlag * old_weight, weight) must not overflow; enforced by the clamping invariant limiting vlag magnitude and old_weight being bounded by the kernel weight scale
- The signed cast (s64)(se->deadline - avruntime) must represent a valid time difference; enforced by deadline and avruntime both being u64 nanosecond timestamps from the same scheduling domain

Invariant verification:
- vlag clamping before scaling multiplication: holds=true. Evidence: entity_lag() computes vlag = avruntime - se->vruntime, then limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se), then returns clamp(vlag, -limit, limit). This clamped result is passed to reweight_eevdf which multiplies it by old_weight in div_s64(vlag * old_weight, weight).
- Safe signed division with zero-divisor protection: holds=true. Evidence: Both vlag and vslice scaling use div_s64() for division by weight. div_s64() is a kernel utility that checks for zero divisors and returns a defined fallback, preventing division-by-zero or undefined behavior.
- Bounded intermediate product magnitude: holds=true. Evidence: Since vlag is clamped to [-limit, limit] and old_weight is an unsigned long bounded by MAX_WEIGHT (1024 in kernel scheduler), the product vlag * old_weight fits within s64 (±2^63). The limit itself is derived from TICK_NSEC and se->slice, both bounded time values.
- vslice computation uses valid time representation: holds=true. Evidence: vslice = (s64)(se->deadline - avruntime) where both se->deadline and avruntime are u64 nanosecond timestamps. The subtraction produces a reasonable time difference that, when scaled by weight ratio, fits within s64 range for normal scheduling parameters.

Residual risk notes: The vslice calculation in the DEADLINE branch does not apply explicit clamping like the vlag path. While vslice represents a bounded scheduling deadline difference in practice, an unusually large deadline value combined with a large old_weight could theoretically approach s64 limits. However, se->deadline is set by the scheduler using avruntime + vslice patterns that keep values in reasonable ranges, and div_s64 provides overflow-safe division semantics. The weight parameter is not validated for zero at the call site, but div_s64 handles this defensively.

Control flow: reweight_eevdf is called during task reweighting. It first checks if avruntime differs from se->vruntime, and if so, computes vlag via entity_lag() and adjusts vruntime. Then it computes vslice from deadline-avruntime and adjusts deadline. Both adjustments use div_s64(vlag/vslice * old_weight, weight) to scale by the weight ratio. The entity_lag() helper clamps the raw lag to [-limit, limit] where limit is derived from calc_delta_fair(max(2*se->slice, TICK_NSEC), se).

Data flow: Inputs: se (sched_entity with vruntime, deadline, load.weight), avruntime (average vruntime from cfs_rq), weight (new weight). The function computes vlag = entity_lag(avruntime, se) which internally computes avruntime - se->vruntime and clamps it to [-limit, limit]. Then vlag is scaled by old_weight/weight ratio via div_s64(vlag * old_weight, weight). Similarly, vslice = se->deadline - avruntime is scaled by the same ratio. Outputs: se->vruntime and se->deadline are updated to their reweighted values.

Verdict: Not Vulnerable

Reviewed region:
[kernel/sched/fair.c]
```c
static void reweight_eevdf(struct sched_entity *se, u64 avruntime,
			   unsigned long weight)
{
	unsigned long old_weight = se->load.weight;
	s64 vlag, vslice;

	/*
	 * VRUNTIME
	 * ========
	 *
	 * COROLLARY #1: The virtual runtime of the entity needs to be
	 * adjusted if re-weight at !0-lag point.
	 *
	 * Proof: For contradiction assume this is not true, so we can
	 * re-weight without changing vruntime at !0-lag point.
	 *
	 *             Weight	VRuntime   Avg-VRuntime
	 *     before    w          v            V
	 *      after    w'         v'           V'
	 *
	 * Since lag needs to be preserved through re-weight:
	 *
	 *	lag = (V - v)*w = (V'- v')*w', where v = v'
	 *	==>	V' = (V - v)*w/w' + v		(1)
	 *
	 * Let W be the total weight of the entities before reweight,
	 * since V' is the new weighted average of entities:
	 *
	 *	V' = (WV + w'v - wv) / (W + w' - w)	(2)
	 *
	 * by using (1) & (2) we obtain:
	 *
	 *	(WV + w'v - wv) / (W + w' - w) = (V - v)*w/w' + v
	 *	==> (WV - Wv+Wv+w'v-wv)/(W+w'-w) = (V - v)*w/w' + v
	 *	==> (WV - Wv)/(W + w' - w) + v = (V - v)*w/w' + v
	 *	==>	(V - v)*W/(W + w' - w) = (V - v)*w/w' (3)
	 *
	 * Since we are doing at !0-lag point which means V != v, we
	 * can simplify (3):
	 *
	 *	==>	W / (W + w' - w) = w / w'
	 *	==>	Ww' = Ww + ww' - ww
	 *	==>	W * (w' - w) = w * (w' - w)
	 *	==>	W = w	(re-weight indicates w' != w)
	 *
	 * So the cfs_rq contains only one entity, hence vruntime of
	 * the entity @v should always equal to the cfs_rq's weighted
	 * average vruntime @V, which means we will always re-weight
	 * at 0-lag point, thus breach assumption. Proof completed.
	 *
	 *
	 * COROLLARY #2: Re-weight does NOT affect weighted average
	 * vruntime of all the entities.
	 *
	 * Proof: According to corollary #1, Eq. (1) should be:
	 *
	 *	(V - v)*w = (V' - v')*w'
	 *	==>	    v' = V' - (V - v)*w/w'		(4)
	 *
	 * According to the weighted average formula, we have:
	 *
	 *	V' = (WV - wv + w'v') / (W - w + w')
	 *	   = (WV - wv + w'(V' - (V - v)w/w')) / (W - w + w')
	 *	   = (WV - wv + w'V' - Vw + wv) / (W - w + w')
	 *	   = (WV + w'V' - Vw) / (W - w + w')
	 *
	 *	==>	V'*(W - w + w') = WV + w'V' - Vw
	 *	==>	V' * (W - w) = (W - w) * V	(5)
	 *
	 * If the entity is the only one in the cfs_rq, then reweight
	 * always occurs at 0-lag point, so V won't change. Or else
	 * there are other entities, hence W != w, then Eq. (5) turns
	 * into V' = V. So V won't change in either case, proof done.
	 *
	 *
	 * So according to corollary #1 & #2, the effect of re-weight
	 * on vruntime should be:
	 *
	 *	v' = V' - (V - v) * w / w'		(4)
	 *	   = V  - (V - v) * w / w'
	 *	   = V  - vl * w / w'
	 *	   = V  - vl'
	 */
	if (avruntime != se->vruntime) {
		vlag = entity_lag(avruntime, se);
		vlag = div_s64(vlag * old_weight, weight);
		se->vruntime = avruntime - vlag;
	}

	/*
	 * DEADLINE
	 * ========
	 *
	 * When the weight changes, the virtual time slope changes and
	 * we should adjust the relative virtual deadline accordingly.
	 *
	 *	d' = v' + (d - v)*w/w'
	 *	   = V' - (V - v)*w/w' + (d - v)*w/w'
	 *	   = V  - (V - v)*w/w' + (d - v)*w/w'
	 *	   = V  + (d - V)*w/w'
	 */
	vslice = (s64)(se->deadline - avruntime);
	vslice = div_s64(vslice * old_weight, weight);
	se->deadline = avruntime + vslice;
}
```

Why it is not vulnerable: The arithmetic operations in the weight adjustment calculations are protected by multiple mechanisms. First, entity_lag() clamps the computed lag to [-limit, limit] where limit is derived from calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se), which bounds vlag to reasonable scheduling time intervals. Second, old_weight is se->load.weight which is bounded by the scheduler's weight table (typically 1 to ~88761 for nice values -20 to 19). The product vlag * old_weight therefore remains within s64 bounds for all practical scheduling scenarios. Similarly, vslice represents a deadline offset bounded by typical scheduling intervals. The div_s64() function safely performs signed 64-bit division.

Security invariants:
- vlag returned by entity_lag() is clamped to [-limit, limit], preventing unbounded values from propagating to the multiplication. Enforced by: clamp(vlag, -limit, limit) in entity_lag().
- old_weight (se->load.weight) is bounded by the scheduler's predefined weight table, limiting the multiplication factor. Enforced by: the load_weight structure's weight field being set from a bounded table during entity initialization.
- limit is derived from calc_delta_fair() which scales the maximum of 2*se->slice and TICK_NSEC by the weight ratio, ensuring the clamp bound is proportional to valid scheduling intervals. Enforced by: limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se) in entity_lag().
- div_s64() performs safe signed 64-bit division, preventing undefined behavior from division operations. Enforced by: the div_s64() kernel helper function.
- The condition (avruntime != se->vruntime) ensures vruntime adjustment only occurs when there is actual lag, avoiding unnecessary computation.

Invariant verification:
- vlag is bounded before multiplication: holds=true. Evidence: entity_lag() returns clamp(vlag, -limit, limit) where limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se). This clamps the raw difference (avruntime - se->vruntime) to a bounded range derived from the entity's slice and tick period.
- old_weight is bounded by scheduler weight table: holds=true. Evidence: old_weight = se->load.weight, where load.weight is set from the scheduler's predefined weight table based on nice values (-20 to 19), with maximum value ~88761.
- Multiplication vlag * old_weight does not overflow s64: holds=true. Evidence: vlag is clamped to [-limit, limit] where limit is proportional to se->slice (typically hundreds of milliseconds in nanoseconds, ~1e14 max). old_weight max is ~88761. The product max is approximately 8.9e18, which is within s64 max of ~9.2e18.
- Division by weight is safe: holds=true. Evidence: weight is passed as a parameter from the caller (reweighting path), which validates weight before calling reweight_eevdf. The div_s64() function handles signed 64-bit division safely.
- vslice calculation is bounded: holds=true. Evidence: vslice = (s64)(se->deadline - avruntime) represents the deadline offset from average vruntime, which is bounded by the scheduler's deadline calculations and typical scheduling intervals.

Residual risk notes: While the multiplication vlag * old_weight and vslice * old_weight are not explicitly checked for overflow with a runtime assertion, the combination of clamping in entity_lag() and the bounded nature of scheduler weights ensures these products remain within s64 bounds for all practical scheduling scenarios. The safety relies on the assumption that se->slice and se->deadline are set to valid values by the scheduler, which is enforced by the scheduler's initialization and update paths.

```json
{
  "vulnerable": false,
  "cwe": null,
  "severity": "NONE",
  "reviewed_region": {
    "file_path": "kernel/sched/fair.c",
    "symbol": "reweight_eevdf",
    "code": "static void reweight_eevdf(struct sched_entity *se, u64 avruntime,\n\t\t\t   unsigned long weight)\n{\n\tunsigned long old_weight = se->load.weight;\n\ts64 vlag, vslice;\n\n\t/*\n\t * VRUNTIME\n\t * ========\n\t *\n\t * COROLLARY #1: The virtual runtime of the entity needs to be\n\t * adjusted if re-weight at !0-lag point.\n\t *\n\t * Proof: For contradiction assume this is not true, so we can\n\t * re-weight without changing vruntime at !0-lag point.\n\t *\n\t *             Weight\tVRuntime   Avg-VRuntime\n\t *     before    w          v            V\n\t *      after    w'         v'           V'\n\t *\n\t * Since lag needs to be preserved through re-weight:\n\t *\n\t *\tlag = (V - v)*w = (V'- v')*w', where v = v'\n\t *\t==>\tV' = (V - v)*w/w' + v\t\t(1)\n\t *\n\t * Let W be the total weight of the entities before reweight,\n\t * since V' is the new weighted average of entities:\n\t *\n\t *\tV' = (WV + w'v - wv) / (W + w' - w)\t(2)\n\t *\n\t * by using (1) & (2) we obtain:\n\t *\n\t *\t(WV + w'v - wv) / (W + w' - w) = (V - v)*w/w' + v\n\t *\t==> (WV - Wv+Wv+w'v-wv)/(W+w'-w) = (V - v)*w/w' + v\n\t *\t==> (WV - Wv)/(W + w' - w) + v = (V - v)*w/w' + v\n\t *\t==>\t(V - v)*W/(W + w' - w) = (V - v)*w/w' (3)\n\t *\n\t * Since we are doing at !0-lag point which means V != v, we\n\t * can simplify (3):\n\t *\n\t *\t==>\tW / (W + w' - w) = w / w'\n\t *\t==>\tWw' = Ww + ww' - ww\n\t *\t==>\tW * (w' - w) = w * (w' - w)\n\t *\t==>\tW = w\t(re-weight indicates w' != w)\n\t *\n\t * So the cfs_rq contains only one entity, hence vruntime of\n\t * the entity @v should always equal to the cfs_rq's weighted\n\t * average vruntime @V, which means we will always re-weight\n\t * at 0-lag point, thus breach assumption. Proof completed.\n\t *\n\t *\n\t * COROLLARY #2: Re-weight does NOT affect weighted average\n\t * vruntime of all the entities.\n\t *\n\t * Proof: According to corollary #1, Eq. (1) should be:\n\t *\n\t *\t(V - v)*w = (V' - v')*w'\n\t *\t==>\t    v' = V' - (V - v)*w/w'\t\t(4)\n\t *\n\t * According to the weighted average formula, we have:\n\t *\n\t *\tV' = (WV - wv + w'v') / (W - w + w')\n\t *\t   = (WV - wv + w'(V' - (V - v)w/w')) / (W - w + w')\n\t *\t   = (WV - wv + w'V' - Vw + wv) / (W - w + w')\n\t *\t   = (WV + w'V' - Vw) / (W - w + w')\n\t *\n\t *\t==>\tV'*(W - w + w') = WV + w'V' - Vw\n\t *\t==>\tV' * (W - w) = (W - w) * V\t(5)\n\t *\n\t * If the entity is the only one in the cfs_rq, then reweight\n\t * always occurs at 0-lag point, so V won't change. Or else\n\t * there are other entities, hence W != w, then Eq. (5) turns\n\t * into V' = V. So V won't change in either case, proof done.\n\t *\n\t *\n\t * So according to corollary #1 & #2, the effect of re-weight\n\t * on vruntime should be:\n\t *\n\t *\tv' = V' - (V - v) * w / w'\t\t(4)\n\t *\t   = V  - (V - v) * w / w'\n\t *\t   = V  - vl * w / w'\n\t *\t   = V  - vl'\n\t */\n\tif (avruntime != se->vruntime) {\n\t\tvlag = entity_lag(avruntime, se);\n\t\tvlag = div_s64(vlag * old_weight, weight);\n\t\tse->vruntime = avruntime - vlag;\n\t}\n\n\t/*\n\t * DEADLINE\n\t * ========\n\t *\n\t * When the weight changes, the virtual time slope changes and\n\t * we should adjust the relative virtual deadline accordingly.\n\t *\n\t *\td' = v' + (d - v)*w/w'\n\t *\t   = V' - (V - v)*w/w' + (d - v)*w/w'\n\t *\t   = V  - (V - v)*w/w' + (d - v)*w/w'\n\t *\t   = V  + (d - V)*w/w'\n\t */\n\tvslice = (s64)(se->deadline - avruntime);\n\tvslice = div_s64(vslice * old_weight, weight);\n\tse->deadline = avruntime + vslice;\n}"
  },
  "vulnerable_region": null,
  "root_cause": null,
  "exploitability": null,
  "attack_preconditions": [],
  "impact": "No concrete vulnerability impact identified in the supplied code and context.",
  "non_vulnerable_reason": "The arithmetic operations in the weight adjustment calculations are protected by multiple mechanisms. First, entity_lag() clamps the computed lag to [-limit, limit] where limit is derived from calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se), which bounds vlag to reasonable scheduling time intervals. Second, old_weight is se->load.weight which is bounded by the scheduler's weight table (typically 1 to ~88761 for nice values -20 to 19). The product vlag * old_weight therefore remains within s64 bounds for all practical scheduling scenarios. Similarly, vslice represents a deadline offset bounded by typical scheduling intervals. The div_s64() function safely performs signed 64-bit division.",
  "security_invariants": [
    "vlag returned by entity_lag() is clamped to [-limit, limit], preventing unbounded values from propagating to the multiplication. Enforced by: clamp(vlag, -limit, limit) in entity_lag().",
    "old_weight (se->load.weight) is bounded by the scheduler's predefined weight table, limiting the multiplication factor. Enforced by: the load_weight structure's weight field being set from a bounded table during entity initialization.",
    "limit is derived from calc_delta_fair() which scales the maximum of 2*se->slice and TICK_NSEC by the weight ratio, ensuring the clamp bound is proportional to valid scheduling intervals. Enforced by: limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se) in entity_lag().",
    "div_s64() performs safe signed 64-bit division, preventing undefined behavior from division operations. Enforced by: the div_s64() kernel helper function.",
    "The condition (avruntime != se->vruntime) ensures vruntime adjustment only occurs when there is actual lag, avoiding unnecessary computation."
  ],
  "invariant_verification": [
    {
      "property": "vlag is bounded before multiplication",
      "holds": true,
      "evidence": "entity_lag() returns clamp(vlag, -limit, limit) where limit = calc_delta_fair(max_t(u64, 2*se->slice, TICK_NSEC), se). This clamps the raw difference (avruntime - se->vruntime) to a bounded range derived from the entity's slice and tick period."
    },
    {
      "property": "old_weight is bounded by scheduler weight table",
      "holds": true,
      "evidence": "old_weight = se->load.weight, where load.weight is set from the scheduler's predefined weight table based on nice values (-20 to 19), with maximum value ~88761."
    },
    {
      "property": "Multiplication vlag * old_weight does not overflow s64",
      "holds": true,
      "evidence": "vlag is clamped to [-limit, limit] where limit is proportional to se->slice (typically hundreds of milliseconds in nanoseconds, ~1e14 max). old_weight max is ~88761. The product max is approximately 8.9e18, which is within s64 max of ~9.2e18."
    },
    {
      "property": "Division by weight is safe",
      "holds": true,
      "evidence": "weight is passed as a parameter from the caller (reweighting path), which validates weight before calling reweight_eevdf. The div_s64() function handles signed 64-bit division safely."
    },
    {
      "property": "vslice calculation is bounded",
      "holds": true,
      "evidence": "vslice = (s64)(se->deadline - avruntime) represents the deadline offset from average vruntime, which is bounded by the scheduler's deadline calculations and typical scheduling intervals."
    }
  ],
  "residual_risk_notes": "While the multiplication vlag * old_weight and vslice * old_weight are not explicitly checked for overflow with a runtime assertion, the combination of clamping in entity_lag() and the bounded nature of scheduler weights ensures these products remain within s64 bounds for all practical scheduling scenarios. The safety relies on the assumption that se->slice and se->deadline are set to valid values by the scheduler, which is enforced by the scheduler's initialization and update paths.",
  "call_path": [
    "reweight_eevdf",
    "entity_eligible",
    "pick_eevdf",
    "update_entity_lag",
    "calc_delta_fair",
    "avg_vruntime"
  ]
}
```


Audit the following candidate code to determine whether it presents a real vulnerability.
This sample may come from a noisy retrieval or transformation stage, so reason from the supplied code and context rather than assuming the label.

Repository: https://github.com/torvalds/linux
Language: C
File: drivers/clk/sunxi-ng/ccu_common.c
Function: sunxi_ccu_probe

Call path: devm_sunxi_ccu_probe (drivers/clk/sunxi-ng/ccu_common.c) → of_sunxi_ccu_probe (drivers/clk/sunxi-ng/ccu_common.c) → sunxi_ccu_probe (drivers/clk/sunxi-ng/ccu_common.c) → hw_to_ccu_common (drivers/clk/sunxi-ng/ccu_common.h)

### Primary Function

```c
static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
			   struct device_node *node, void __iomem *reg,
			   const struct sunxi_ccu_desc *desc)
{
	struct ccu_reset *reset;
	int i, ret;

	ccu->desc = desc;

	spin_lock_init(&ccu->lock);

	for (i = 0; i < desc->num_ccu_clks; i++) {
		struct ccu_common *cclk = desc->ccu_clks[i];

		if (!cclk)
			continue;

		cclk->base = reg;
		cclk->lock = &ccu->lock;
	}

	for (i = 0; i < desc->hw_clks->num ; i++) {
		struct clk_hw *hw = desc->hw_clks->hws[i];
		struct ccu_common *common = hw_to_ccu_common(hw);
		const char *name;

		if (!hw)
			continue;

		name = hw->init->name;
		if (dev)
			ret = clk_hw_register(dev, hw);
		else
			ret = of_clk_hw_register(node, hw);
		if (ret) {
			pr_err("Couldn't register clock %d - %s\n", i, name);
			goto err_clk_unreg;
		}

		if (common->max_rate)
			clk_hw_set_rate_range(hw, common->min_rate,
					      common->max_rate);
		else
			WARN(common->min_rate,
			     "No max_rate, ignoring min_rate of clock %d - %s\n",
			     i, name);
	}

	ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
				     desc->hw_clks);
	if (ret)
		goto err_clk_unreg;

	reset = &ccu->reset;
	reset->rcdev.of_node = node;
	reset->rcdev.ops = &ccu_reset_ops;
	reset->rcdev.owner = dev ? dev->driver->owner : THIS_MODULE;
	reset->rcdev.nr_resets = desc->num_resets;
	reset->base = reg;
	reset->lock = &ccu->lock;
	reset->reset_map = desc->resets;

	ret = reset_controller_register(&reset->rcdev);
	if (ret)
		goto err_del_provider;

	return 0;

err_del_provider:
	of_clk_del_provider(node);
err_clk_unreg:
	while (--i >= 0) {
		struct clk_hw *hw = desc->hw_clks->hws[i];

		if (!hw)
			continue;
		clk_hw_unregister(hw);
	}
	return ret;
}
```

### Cross-File Context

[hw_to_ccu_common — helper — drivers/clk/sunxi-ng/ccu_common.h:42]
hw_to_ccu_common → static inline struct ccu_common *hw_to_ccu_common(struct clk_hw *hw) { return container_of(hw, struct ccu_common, hw); }  (drivers/clk/sunxi-ng/ccu_common.h:42)

[ccu_common — struct — drivers/clk/sunxi-ng/ccu_common.h:28]
```c
struct ccu_common {
	void __iomem	*base;
	u16		reg;
	u16		lock_reg;
	u32		prediv;

	unsigned long	min_rate;
	unsigned long	max_rate;

	unsigned long	features;
	spinlock_t	*lock;
	struct clk_hw	hw;
};
```

[sunxi_ccu_desc — struct — drivers/clk/sunxi-ng/ccu_common.h:47]
```c
struct sunxi_ccu_desc {
	struct ccu_common		**ccu_clks;
	unsigned long			num_ccu_clks;

	struct clk_hw_onecell_data	*hw_clks;

	struct ccu_reset_map		*resets;
	unsigned long			num_resets;
};
```

[clk_hw_set_rate_range — function — include/linux/clk-provider.h]
```c
void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate, unsigned long max_rate)
```

Control flow: sunxi_ccu_probe iterates over hw_clks->hws[] to register clocks. In the vulnerable version, it calls hw_to_ccu_common on each hw entry and immediately dereferences the result. The fix separates clock registration from rate range setting, using a second loop over ccu_clks[] which are guaranteed to be ccu_common structs.

Data flow: The attacker-controlled data flows from DeviceTree descriptor (desc->hw_clks->hws[]) through hw_to_ccu_common() which performs an unchecked type cast via container_of. The resulting pointer is dereferenced to access min_rate and max_rate fields without validation.

Verdict: Vulnerable
Vulnerable region:
[drivers/clk/sunxi-ng/ccu_common.c]
```c
for (i = 0; i < desc->hw_clks->num ; i++) {
		struct clk_hw *hw = desc->hw_clks->hws[i];
		struct ccu_common *common = hw_to_ccu_common(hw);
		const char *name;

		if (!hw)
			continue;

		name = hw->init->name;
		if (dev)
			ret = clk_hw_register(dev, hw);
		else
			ret = of_clk_hw_register(node, hw);
		if (ret) {
			pr_err("Couldn't register clock %d - %s\n", i, name);
			goto err_clk_unreg;
		}

		if (common->max_rate)
			clk_hw_set_rate_range(hw, common->min_rate,
					      common->max_rate);
		else
			WARN(common->min_rate,
			     "No max_rate, ignoring min_rate of clock %d - %s\n",
			     i, name);
	}

	ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
				     desc->hw_clks);
	if (ret)
		goto err_clk_unreg;
```
CWE: CWE-476
Severity: MEDIUM

Root cause: The sunxi_ccu_probe function incorrectly assumes all entries in desc->hw_clks->hws[] are ccu_common structs by using hw_to_ccu_common(hw) which is a container_of cast. When hw points to a non-ccu_common clk_hw, the cast produces an invalid pointer, and subsequent access to common->max_rate or common->min_rate causes an invalid pointer dereference.

Exploitability: The vulnerability is triggered during device probe when a sunxi CCU clock device has a hw_clks->hws[] array containing entries that are not ccu_common structs. The buggy code causes immediate dereference of an invalid pointer constructed via container_of on the wrong type.

Attack preconditions:
- Target must have a DeviceTree node with a sunxi CCU clock controller
- The ccu_clks array in the device descriptor must contain at least one non-ccu_common clk_hw entry
- The clock driver probe function (sunxi_ccu_probe or its wrappers) must be invoked

Likely impact: The vulnerability is triggered during device probe when a sunxi CCU clock device has a hw_clks->hws[] array containing entries that are not ccu_common structs. The buggy code causes immediate dereference of an invalid pointer constructed via container_of on the wrong type.

Control flow: The function initializes the ccu descriptor and spinlock, then iterates over desc->ccu_clks to set base and lock pointers for each non-null entry. A second loop iterates over desc->hw_clks->hws to register each clock hardware, setting rate ranges where applicable. After clock registration, it adds a clock hw provider, configures and registers a reset controller. Two error paths exist: err_del_provider unregisters the clock provider, and err_clk_unreg walks backwards through registered clocks to unregister them before returning the error code.

Data flow: Input parameters (ccu, dev, node, reg, desc) are used to populate internal structures. desc->ccu_clks entries are validated for NULL before field access. desc->hw_clks->hws entries are validated for NULL before dereferencing. hw->init->name is accessed after hw is validated but before hw->init is validated. common (derived from hw via container_of) fields max_rate and min_rate are read to configure rate constraints. Reset controller fields are populated from desc and node before registration.

Verdict: Not Vulnerable

Reviewed region:
[drivers/clk/sunxi-ng/ccu_common.c]
```c
static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
			   struct device_node *node, void __iomem *reg,
			   const struct sunxi_ccu_desc *desc)
{
	struct ccu_reset *reset;
	int i, ret;

	ccu->desc = desc;

	spin_lock_init(&ccu->lock);

	for (i = 0; i < desc->num_ccu_clks; i++) {
		struct ccu_common *cclk = desc->ccu_clks[i];

		if (!cclk)
			continue;

		cclk->base = reg;
		cclk->lock = &ccu->lock;
	}

	for (i = 0; i < desc->hw_clks->num ; i++) {
		struct clk_hw *hw = desc->hw_clks->hws[i];
		struct ccu_common *common = hw_to_ccu_common(hw);
		const char *name;

		if (!hw)
			continue;

		name = hw->init->name;
		if (dev)
			ret = clk_hw_register(dev, hw);
		else
			ret = of_clk_hw_register(node, hw);
		if (ret) {
			pr_err("Couldn't register clock %d - %s\n", i, name);
			goto err_clk_unreg;
		}

		if (common->max_rate)
			clk_hw_set_rate_range(hw, common->min_rate,
					      common->max_rate);
		else
			WARN(common->min_rate,
			     "No max_rate, ignoring min_rate of clock %d - %s\n",
			     i, name);
	}

	ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
				     desc->hw_clks);
	if (ret)
		goto err_clk_unreg;

	reset = &ccu->reset;
	reset->rcdev.of_node = node;
	reset->rcdev.ops = &ccu_reset_ops;
	reset->rcdev.owner = dev ? dev->driver->owner : THIS_MODULE;
	reset->rcdev.nr_resets = desc->num_resets;
	reset->base = reg;
	reset->lock = &ccu->lock;
	reset->reset_map = desc->resets;

	ret = reset_controller_register(&reset->rcdev);
	if (ret)
		goto err_del_provider;

	return 0;

err_del_provider:
	of_clk_del_provider(node);
err_clk_unreg:
	while (--i >= 0) {
		struct clk_hw *hw = desc->hw_clks->hws[i];

		if (!hw)
			continue;
		clk_hw_unregister(hw);
	}
	return ret;
}
```

Why it is not vulnerable: The code performs explicit null checks before dereferencing pointers that originate from descriptor arrays: `if (!cclk) continue;` guards cclk->base and cclk->lock access, and `if (!hw) continue;` guards hw->init->name and hw registration calls. Loop bounds are controlled by desc->num_ccu_clks and desc->hw_clks->num respectively. Error paths correctly unwind previously registered resources. The desc parameter and its sub-fields (hw_clks, ccu_clks) are assumed valid as they originate from static driver descriptions provided at compile time.

Security invariants:
- cclk must be non-NULL before accessing cclk->base and cclk->lock; enforced by `if (!cclk) continue;` in the first loop
- hw must be non-NULL before accessing hw->init->name and calling clk_hw_register; enforced by `if (!hw) continue;` in the second loop
- Loop indices must not exceed array bounds; enforced by `i < desc->num_ccu_clks` and `i < desc->hw_clks->num` loop conditions
- Error paths must clean up all previously registered resources; enforced by err_del_provider calling of_clk_del_provider and err_clk_unreg iterating backwards to clk_hw_unregister each clock
- desc and desc->hw_clks must be valid non-NULL pointers; enforced by caller providing statically-defined descriptor structures

Invariant verification:
- Null check on cclk before dereference: holds=true. Evidence: The statement `if (!cclk) continue;` immediately follows `struct ccu_common *cclk = desc->ccu_clks[i];` and precedes `cclk->base = reg;` and `cclk->lock = &ccu->lock;`
- Null check on hw before dereference: holds=true. Evidence: The statement `if (!hw) continue;` immediately follows `struct clk_hw *hw = desc->hw_clks->hws[i];` and precedes `name = hw->init->name;` and the clk_hw_register call
- Loop bounds validation: holds=true. Evidence: First loop uses `i < desc->num_ccu_clks` and second loop uses `i < desc->hw_clks->num` as termination conditions
- Error path resource cleanup: holds=true. Evidence: err_clk_unreg label contains `while (--i >= 0)` loop that calls `clk_hw_unregister(hw)` for each previously registered clock; err_del_provider calls `of_clk_del_provider(node)`
- hw->init non-NULL before accessing hw->init->name: holds=false. Evidence: The code checks `if (!hw)` but does not explicitly check `if (!hw->init)` before executing `name = hw->init->name;`

Residual risk notes: The code accesses `hw->init->name` without explicitly verifying that `hw->init` is non-NULL, relying on the assumption that all statically-defined clock hardware structures have valid init pointers. Additionally, `desc->hw_clks` is dereferenced without a NULL check (accessing `desc->hw_clks->num`), and `desc->ccu_clks` is dereferenced without a NULL check (accessing `desc->ccu_clks[i]`), both relying on caller guarantees that the descriptor structure is fully populated. These assumptions are reasonable for statically-defined kernel driver descriptors but represent implicit trust in the descriptor integrity.