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/apache/mynewt-nimble
Language: C
File: nimble/host/src/ble_hs_hci_evt.c
Function: ble_hs_hci_evt_le_adv_rpt_first_pass

Call path: ble_hs_hci_evt_le_adv_rpt (nimble/host/src/ble_hs_hci_evt.c) → ble_hs_hci_evt_le_adv_rpt_first_pass (nimble/host/src/ble_hs_hci_evt.c) → ble_gap_rx_adv_report (nimble/host/src/ble_gap.c)

### Primary Function

```c
static int
ble_hs_hci_evt_le_adv_rpt_first_pass(const void *data, unsigned int len)
{
    const struct ble_hci_ev_le_subev_adv_rpt *ev = data;
    const struct adv_report *rpt;
    int i;

    if (len < sizeof(*ev)) {
        return BLE_HS_ECONTROLLER;
    }

    len -= sizeof(*ev);
    data += sizeof(*ev);

    if (ev->num_reports < BLE_HCI_LE_ADV_RPT_NUM_RPTS_MIN ||
        ev->num_reports > BLE_HCI_LE_ADV_RPT_NUM_RPTS_MAX) {
        return BLE_HS_EBADDATA;
    }

    for (i = 0; i < ev->num_reports; i++) {
        /* extra byte for RSSI after adv data */
        if (len < sizeof(*rpt) + 1) {
            return BLE_HS_ECONTROLLER;
        }

        rpt = data;

        len -= sizeof(*rpt) + 1;
        data += sizeof(rpt) + 1;

        if (rpt->data_len > len) {
            return BLE_HS_ECONTROLLER;
        }

        len -= rpt->data_len;
        data += rpt->data_len;
    }

    /* Make sure length was correct */
    if (len) {
        return BLE_HS_ECONTROLLER;
    }

    return 0;
}
```

### Cross-File Context

[adv_report — struct — nimble/include/nimble/hci_common.h:1860]
```c
struct adv_report {
    uint8_t type;
    uint8_t addr_type;
    uint8_t addr[6];
    uint8_t data_len;
    uint8_t data[0];
} __attribute__((packed));
```

[ble_hci_ev_le_subev_adv_rpt — struct — nimble/include/nimble/hci_common.h:1867]
```c
struct ble_hci_ev_le_subev_adv_rpt {
    uint8_t  subev_code;
    uint8_t  num_reports;
    struct adv_report reports[0];
} __attribute__((packed));
```

[BLE_HS_ECONTROLLER — constant — nimble/host/include/host/ble_hs.h:103]
BLE_HS_ECONTROLLER → 12  (nimble/host/include/host/ble_hs.h:103)

[BLE_HS_EBADDATA — constant — nimble/host/include/host/ble_hs.h:97]
BLE_HS_EBADDATA → 10  (nimble/host/include/host/ble_hs.h:97)

[BLE_HCI_LE_ADV_RPT_NUM_RPTS_MIN — constant — nimble/include/nimble/hci_common.h:2363]
BLE_HCI_LE_ADV_RPT_NUM_RPTS_MIN → (1)  (nimble/include/nimble/hci_common.h:2363)

[BLE_HCI_LE_ADV_RPT_NUM_RPTS_MAX — constant — nimble/include/nimble/hci_common.h:2364]
BLE_HCI_LE_ADV_RPT_NUM_RPTS_MAX → (0x19)  (nimble/include/nimble/hci_common.h:2364)

[ble_hs_hci_evt_le_adv_rpt — caller — nimble/host/src/ble_hs_hci_evt.c:505]
```c
static int
ble_hs_hci_evt_le_adv_rpt(uint8_t subevent, const void *data, unsigned int len)
{
    const struct ble_hci_ev_le_subev_adv_rpt *ev = data;
    struct ble_gap_disc_desc desc = {0};
    const struct adv_report *rpt;
    int rc;
    int i;

    /* Validate the event is formatted correctly */
    rc = ble_hs_hci_evt_le_adv_rpt_first_pass(data, len);
    if (rc != 0) {
        return rc;
    }

    data += sizeof(*ev);

    desc.direct_addr = *BLE_ADDR_ANY;

    for (i = 0; i < ev->num_reports; i++) {
        rpt = data;

        data += sizeof(*rpt) + rpt->data_len + 1;

        desc.event_type = rpt->type;
        desc.addr.type = rpt->addr_type;
        memcpy(desc.addr.val, rpt->addr, BLE_DEV_ADDR_LEN);
        desc.length_data = rpt->data_len;
        desc.data = rpt->data;
        desc.rssi = rpt->data[rpt->data_len];

        ble_gap_rx_adv_report(&desc);
    }

    return 0;
}
```

Control flow: ble_hs_hci_evt_le_adv_rpt calls ble_hs_hci_evt_le_adv_rpt_first_pass for validation. If validation passes (returns 0), the function proceeds to iterate over each report and call ble_gap_rx_adv_report for each. The vulnerable function loops over num_reports and performs pointer arithmetic to advance through the report data.

Data flow: Raw HCI event data flows from the controller through ble_hs_hci_evt_le_adv_rpt_first_pass for validation, then through ble_hs_hci_evt_le_adv_rpt for parsing. The bug affects the len (remaining length) and data (current position) variables used to track buffer boundaries during multi-report parsing.

Verdict: Vulnerable
Vulnerable region:
[nimble/host/src/ble_hs_hci_evt.c]
```c
rpt = data;

        len -= sizeof(*rpt) + 1;
        data += sizeof(rpt) + 1;

        if (rpt->data_len > len) {
            return BLE_HS_ECONTROLLER;
        }

        len -= rpt->data_len;
        data += rpt->data_len;
    }
```
CWE: CWE-125
Severity: MEDIUM

Root cause: In ble_hs_hci_evt_le_adv_rpt_first_pass, the expression 'data += sizeof(rpt) + 1' uses sizeof(rpt) which is the size of the pointer (8 bytes on 64-bit systems) instead of sizeof(*rpt) which is the actual size of the struct adv_report (9 bytes: 1+1+6+1 for type+addr_type+addr+data_len). This incorrect pointer arithmetic causes the validation to fail when processing events with more than one advertising report because the accumulated offset error compounds with each report processed.

Exploitability: The vulnerability is triggered when receiving a Bluetooth HCI event containing more than one advertising report. The controller sends multi-report events which fail validation due to the pointer arithmetic error, causing legitimate advertising data to be discarded.

Attack preconditions:
- Attacker must be able to send HCI events to the affected device
- The HCI event must contain more than one advertising report (num_reports >= 2)
- The event must be a legacy advertising report event (LE Advertising Report)

Likely impact: The vulnerability is triggered when receiving a Bluetooth HCI event containing more than one advertising report. The controller sends multi-report events which fail validation due to the pointer arithmetic error, causing legitimate advertising data to be discarded.

Control flow: The function validates a BLE advertising report HCI event in a single pass. It first checks that the total length is at least the size of the event header, then advances past the header. It validates num_reports is in [1, 0x19]. For each report, it checks sufficient bytes remain for the report header plus RSSI, advances past the header, checks that the report data length does not exceed remaining bytes, and advances past the data. After the loop, it verifies no trailing bytes remain. Error paths return BLE_HS_ECONTROLLER or BLE_HS_EBADDATA at various validation failure points.

Data flow: Input data pointer and length are consumed to validate the HCI event structure. The event header is validated first, then each advertising report's header (type, addr_type, addr, data_len) and payload are checked for bounds. The rpt->data_len field from the event is used to determine how many bytes of advertising data follow each report header. The validation ensures all bytes in the input are accounted for with no over-read or trailing data.

Verdict: Not Vulnerable

Reviewed region:
[nimble/host/src/ble_hs_hci_evt.c]
```c
static int
ble_hs_hci_evt_le_adv_rpt_first_pass(const void *data, unsigned int len)
{
    const struct ble_hci_ev_le_subev_adv_rpt *ev = data;
    const struct adv_report *rpt;
    int i;

    if (len < sizeof(*ev)) {
        return BLE_HS_ECONTROLLER;
    }

    len -= sizeof(*ev);
    data += sizeof(*ev);

    if (ev->num_reports < BLE_HCI_LE_ADV_RPT_NUM_RPTS_MIN ||
        ev->num_reports > BLE_HCI_LE_ADV_RPT_NUM_RPTS_MAX) {
        return BLE_HS_EBADDATA;
    }

    for (i = 0; i < ev->num_reports; i++) {
        /* extra byte for RSSI after adv data */
        if (len < sizeof(*rpt) + 1) {
            return BLE_HS_ECONTROLLER;
        }

        rpt = data;

        len -= sizeof(*rpt) + 1;
        data += sizeof(rpt) + 1;

        if (rpt->data_len > len) {
            return BLE_HS_ECONTROLLER;
        }

        len -= rpt->data_len;
        data += rpt->data_len;
    }

    /* Make sure length was correct */
    if (len) {
        return BLE_HS_ECONTROLLER;
    }

    return 0;
}
```

Why it is not vulnerable: The function performs comprehensive bounds validation on all variable-length fields in the HCI event. The num_reports count is bounded to [1, 0x19] (line: `ev->num_reports < BLE_HCI_LE_ADV_RPT_NUM_RPTS_MIN || ev->num_reports > BLE_HCI_LE_ADV_RPT_NUM_RPTS_MAX`). For each report, the code checks that sufficient bytes remain for the report header plus RSSI byte (line: `if (len < sizeof(*rpt) + 1)`), then validates that rpt->data_len does not exceed the remaining buffer (line: `if (rpt->data_len > len)`). The final check (line: `if (len)`) ensures no unaccounted trailing bytes exist. All pointer arithmetic and length subtractions are guarded by prior bounds checks, preventing buffer over-reads during validation.

Security invariants:
- Total input length must be at least the event header size: enforced by `if (len < sizeof(*ev))` at function entry
- num_reports must be within valid range [1, 0x19]: enforced by `if (ev->num_reports < BLE_HCI_LE_ADV_RPT_NUM_RPTS_MIN || ev->num_reports > BLE_HCI_LE_ADV_RPT_NUM_RPTS_MAX)`
- Each report must have sufficient bytes for header plus RSSI: enforced by `if (len < sizeof(*rpt) + 1)` at loop start
- Report data length must not exceed remaining buffer: enforced by `if (rpt->data_len > len)` before advancing past data
- No trailing bytes should remain after processing all reports: enforced by `if (len)` after the loop
- Length variable must always accurately reflect remaining bytes to prevent underflow: enforced by subtracting the same amount from len as is advanced in data pointer

Invariant verification:
- Input length bounds check before header access: holds=true. Evidence: Line `if (len < sizeof(*ev)) { return BLE_HS_ECONTROLLER; }` prevents reading the event header when insufficient data is present
- Report count validation prevents excessive iterations: holds=true. Evidence: Lines `if (ev->num_reports < BLE_HCI_LE_ADV_RPT_NUM_RPTS_MIN || ev->num_reports > BLE_HCI_LE_ADV_RPT_NUM_RPTS_MAX) { return BLE_HS_EBADDATA; }` bound iterations to [1, 25]
- Per-report header size validation: holds=true. Evidence: Line `if (len < sizeof(*rpt) + 1) { return BLE_HS_ECONTROLLER; }` ensures at least 9 bytes (8-byte header + 1 RSSI byte) remain before accessing rpt fields
- Report payload length bounded by remaining buffer: holds=true. Evidence: Line `if (rpt->data_len > len) { return BLE_HS_ECONTROLLER; }` prevents rpt->data_len from exceeding available bytes, and subsequent `len -= rpt->data_len; data += rpt->data_len;` advances consistently
- No trailing data after all reports consumed: holds=true. Evidence: Line `if (len) { return BLE_HS_ECONTROLLER; }` after the loop ensures the input length exactly matches the parsed content

Residual risk notes: The expression `data += sizeof(rpt) + 1` uses sizeof on the pointer variable rather than the dereferenced struct (sizeof(*rpt)). While sizeof(*rpt) is correctly used for the corresponding len subtraction, this mismatch between sizeof(rpt) (pointer size, typically 4 or 8 bytes) and sizeof(*rpt) (struct size, 8 bytes) causes the data pointer to advance by a different amount than len is decremented on 32-bit architectures. This inconsistency means the len variable and data pointer become desynchronized during iteration, potentially causing the subsequent bounds checks to operate on incorrect assumptions about remaining buffer space. On 64-bit systems, sizeof(rpt) equals sizeof(*rpt), so the behavior happens to be correct. This is an architectural inconsistency that may affect validation accuracy on 32-bit platforms.


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: git.kernel.org/pub/scm/linux/kernel/git/stable/linux
Language: C
File: kernel/bpf/verifier.c
Function: adjust_scalar_min_max_vals

Call path: bpf_prog_load (kernel/bpf/syscall.c) → bpf_check (kernel/bpf/verifier.c) → check_alu_op (kernel/bpf/verifier.c) → adjust_scalar_min_max_vals (kernel/bpf/verifier.c) → adjust_ptr_min_max_vals (kernel/bpf/verifier.c)

### Primary Function

```c
static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
				      struct bpf_insn *insn,
				      struct bpf_reg_state *dst_reg,
				      struct bpf_reg_state src_reg)
{
	struct bpf_reg_state *regs = cur_regs(env);
	u8 opcode = BPF_OP(insn->code);
	bool src_known;
	s64 smin_val, smax_val;
	u64 umin_val, umax_val;
	s32 s32_min_val, s32_max_val;
	u32 u32_min_val, u32_max_val;
	u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
	bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
	int ret;

	smin_val = src_reg.smin_value;
	smax_val = src_reg.smax_value;
	umin_val = src_reg.umin_value;
	umax_val = src_reg.umax_value;

	s32_min_val = src_reg.s32_min_value;
	s32_max_val = src_reg.s32_max_value;
	u32_min_val = src_reg.u32_min_value;
	u32_max_val = src_reg.u32_max_value;

	if (alu32) {
		src_known = tnum_subreg_is_const(src_reg.var_off);
		if ((src_known &&
		     (s32_min_val != s32_max_val || u32_min_val != u32_max_val)) ||
		    s32_min_val > s32_max_val || u32_min_val > u32_max_val) {
			/* Taint dst register if offset had invalid bounds
			 * derived from e.g. dead branches.
			 */
			__mark_reg_unknown(env, dst_reg);
			return 0;
		}
	} else {
		src_known = tnum_is_const(src_reg.var_off);
		if ((src_known &&
		     (smin_val != smax_val || umin_val != umax_val)) ||
		    smin_val > smax_val || umin_val > umax_val) {
			/* Taint dst register if offset had invalid bounds
			 * derived from e.g. dead branches.
			 */
			__mark_reg_unknown(env, dst_reg);
			return 0;
		}
	}

	if (!src_known &&
	    opcode != BPF_ADD && opcode != BPF_SUB && opcode != BPF_AND) {
		__mark_reg_unknown(env, dst_reg);
		return 0;
	}

	if (sanitize_needed(opcode)) {
		ret = sanitize_val_alu(env, insn);
		if (ret < 0)
			return sanitize_err(env, insn, ret, NULL, NULL);
	}

	/* Calculate sign/unsigned bounds and tnum for alu32 and alu64 bit ops.
	 * There are two classes of instructions: The first class we track both
	 * alu32 and alu64 sign/unsigned bounds independently this provides the
	 * greatest amount of precision when alu operations are mixed with jmp32
	 * operations. These operations are BPF_ADD, BPF_SUB, BPF_MUL, BPF_ADD,
	 * and BPF_OR. This is possible because these ops have fairly easy to
	 * understand and calculate behavior in both 32-bit and 64-bit alu ops.
	 * See alu32 verifier tests for examples. The second class of
	 * operations, BPF_LSH, BPF_RSH, and BPF_ARSH, however are not so easy
	 * with regards to tracking sign/unsigned bounds because the bits may
	 * cross subreg boundaries in the alu64 case. When this happens we mark
	 * the reg unbounded in the subreg bound space and use the resulting
	 * tnum to calculate an approximation of the sign/unsigned bounds.
	 */
	switch (opcode) {
	case BPF_ADD:
		scalar32_min_max_add(dst_reg, &src_reg);
		scalar_min_max_add(dst_reg, &src_reg);
		dst_reg->var_off = tnum_add(dst_reg->var_off, src_reg.var_off);
		break;
	case BPF_SUB:
		scalar32_min_max_sub(dst_reg, &src_reg);
		scalar_min_max_sub(dst_reg, &src_reg);
		dst_reg->var_off = tnum_sub(dst_reg->var_off, src_reg.var_off);
		break;
	case BPF_MUL:
		dst_reg->var_off = tnum_mul(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_mul(dst_reg, &src_reg);
		scalar_min_max_mul(dst_reg, &src_reg);
		break;
	case BPF_AND:
		dst_reg->var_off = tnum_and(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_and(dst_reg, &src_reg);
		scalar_min_max_and(dst_reg, &src_reg);
		break;
	case BPF_OR:
		dst_reg->var_off = tnum_or(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_or(dst_reg, &src_reg);
		scalar_min_max_or(dst_reg, &src_reg);
		break;
	case BPF_XOR:
		dst_reg->var_off = tnum_xor(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_xor(dst_reg, &src_reg);
		scalar_min_max_xor(dst_reg, &src_reg);
		break;
	case BPF_LSH:
		if (umax_val >= insn_bitness) {
			/* Shifts greater than 31 or 63 are undefined.
			 * This includes shifts by a negative number.
			 */
			mark_reg_unknown(env, regs, insn->dst_reg);
			break;
		}
		if (alu32)
			scalar32_min_max_lsh(dst_reg, &src_reg);
		else
			scalar_min_max_lsh(dst_reg, &src_reg);
		break;
	case BPF_RSH:
		if (umax_val >= insn_bitness) {
			/* Shifts greater than 31 or 63 are undefined.
			 * This includes shifts by a negative number.
			 */
			mark_reg_unknown(env, regs, insn->dst_reg);
			break;
		}
		if (alu32)
			scalar32_min_max_rsh(dst_reg, &src_reg);
		else
			scalar_min_max_rsh(dst_reg, &src_reg);
		break;
	case BPF_ARSH:
		if (umax_val >= insn_bitness) {
			/* Shifts greater than 31 or 63 are undefined.
			 * This includes shifts by a negative number.
			 */
			mark_reg_unknown(env, regs, insn->dst_reg);
			break;
		}
		if (alu32)
			scalar32_min_max_arsh(dst_reg, &src_reg);
		else
			scalar_min_max_arsh(dst_reg, &src_reg);
		break;
	default:
		mark_reg_unknown(env, regs, insn->dst_reg);
		break;
	}

	/* ALU32 ops are zero extended into 64bit register */
	if (alu32)
		zext_32_to_64(dst_reg);
	reg_bounds_sync(dst_reg);
	return 0;
}
```

### Cross-File Context

[bpf_reg_state — struct — include/linux/bpf_verifier.h:46]
```c
struct bpf_reg_state {
	enum bpf_reg_type type;
	s32 off;
	union { ... };
	u32 id;
	u32 ref_obj_id;
	struct tnum var_off;
	s64 smin_value;
	s64 smax_value;
	u64 umin_value;
	u64 umax_value;
	s32 s32_min_value;
	s32 s32_max_value;
	u32 u32_min_value;
	u32 u32_max_value;
	struct bpf_reg_state *parent;
	...}
```

[tnum — struct — include/linux/tnum.h:14]
```c
struct tnum {
	u64 value;
	u64 mask;
};
```

[__update_reg_bounds — function — kernel/bpf/verifier.c:1471]
```c
static void __update_reg_bounds(struct bpf_reg_state *reg)
{
	__update_reg32_bounds(reg);
	__update_reg64_bounds(reg);
}
```

[__reg_deduce_bounds — function — kernel/bpf/verifier.c:1546]
```c
static void __reg_deduce_bounds(struct bpf_reg_state *reg)
{
	__reg32_deduce_bounds(reg);
	__reg64_deduce_bounds(reg);
}
```

[__reg_bound_offset — function — kernel/bpf/verifier.c:1553]
```c
static void __reg_bound_offset(struct bpf_reg_state *reg)
{
	struct tnum var64_off = tnum_intersect(reg->var_off,
					       tnum_range(reg->umin_value,
							  reg->umax_value));
	struct tnum var32_off = tnum_intersect(tnum_subreg(reg->var_off),
					tnum_range(reg->u32_min_value,
						   reg->u32_max_value));

	reg->var_off = tnum_or(tnum_clear_subreg(var64_off), var32_off);
}
```

[reg_bounds_sync — function — kernel/bpf/verifier.c:1565]
```c
static void reg_bounds_sync(struct bpf_reg_state *reg)
{
	/* We might have learned new bounds from the var_off. */
	__update_reg_bounds(reg);
	/* We might have learned something about the sign bit. */
	__reg_deduce_bounds(reg);
	/* We might have learned some bits from the bounds. */
	__reg_bound_offset(reg);
	/* Intersecting with the old var_off might have improved our bounds
	 * slightly, e.g. if umax was 0x7f...f and var_off was (0; 0xf...fc),
	 * then new var_off is (0; 0x7f...fc) which improves our umax.
	 */
	__update_reg_bounds(reg);
}
```

[SCALAR_VALUE — enum — include/linux/bpf.h:557]
SCALAR_VALUE, /* reg doesn't contain a valid pointer */

[BPF_OP — macro — include/uapi/linux/bpf_common.h:31]
BPF_OP → #define BPF_OP(code) ((code) & 0xf0)  (include/uapi/linux/bpf_common.h:31)

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 extracts min/max bounds from the source register, then validates bounds consistency (checking smin<=smax, umin<=umax, and constancy constraints). If bounds are invalid, it marks dst_reg unknown and returns 0. If source is unknown for non-ADD/SUB/AND operations, it marks dst unknown and returns 0. The function then optionally sanitizes the instruction. A switch statement on opcode dispatches to operation-specific bound calculations (ADD, SUB, MUL, AND, OR, XOR, LSH, RSH, ARSH) or marks the register unknown for default/unsupported cases. Shift operations check that shift amounts don't exceed the instruction bitness. After the switch, ALU32 operations are zero-extended, then reg_bounds_sync is called before returning 0.

Data flow: Input: src_reg provides smin/smax/umin/umax/s32_min/s32_max/u32_min/u32_max bounds and var_off tnum. The instruction opcode determines the ALU operation. These inputs flow through operation-specific scalar32_min_max_* and scalar_min_max_* functions that compute new dst_reg bounds, and tnum_* operations that update dst_reg->var_off. Output: dst_reg's bound fields and var_off are updated, then synchronized via reg_bounds_sync which propagates information between var_off tnum and the scalar min/max fields.

Verdict: Not Vulnerable

Reviewed region:
[kernel/bpf/verifier.c]
```c
static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
				      struct bpf_insn *insn,
				      struct bpf_reg_state *dst_reg,
				      struct bpf_reg_state src_reg)
{
	struct bpf_reg_state *regs = cur_regs(env);
	u8 opcode = BPF_OP(insn->code);
	bool src_known;
	s64 smin_val, smax_val;
	u64 umin_val, umax_val;
	s32 s32_min_val, s32_max_val;
	u32 u32_min_val, u32_max_val;
	u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
	bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
	int ret;

	smin_val = src_reg.smin_value;
	smax_val = src_reg.smax_value;
	umin_val = src_reg.umin_value;
	umax_val = src_reg.umax_value;

	s32_min_val = src_reg.s32_min_value;
	s32_max_val = src_reg.s32_max_value;
	u32_min_val = src_reg.u32_min_value;
	u32_max_val = src_reg.u32_max_value;

	if (alu32) {
		src_known = tnum_subreg_is_const(src_reg.var_off);
		if ((src_known &&
		     (s32_min_val != s32_max_val || u32_min_val != u32_max_val)) ||
		    s32_min_val > s32_max_val || u32_min_val > u32_max_val) {
			/* Taint dst register if offset had invalid bounds
			 * derived from e.g. dead branches.
			 */
			__mark_reg_unknown(env, dst_reg);
			return 0;
		}
	} else {
		src_known = tnum_is_const(src_reg.var_off);
		if ((src_known &&
		     (smin_val != smax_val || umin_val != umax_val)) ||
		    smin_val > smax_val || umin_val > umax_val) {
			/* Taint dst register if offset had invalid bounds
			 * derived from e.g. dead branches.
			 */
			__mark_reg_unknown(env, dst_reg);
			return 0;
		}
	}

	if (!src_known &&
	    opcode != BPF_ADD && opcode != BPF_SUB && opcode != BPF_AND) {
		__mark_reg_unknown(env, dst_reg);
		return 0;
	}

	if (sanitize_needed(opcode)) {
		ret = sanitize_val_alu(env, insn);
		if (ret < 0)
			return sanitize_err(env, insn, ret, NULL, NULL);
	}

	/* Calculate sign/unsigned bounds and tnum for alu32 and alu64 bit ops.
	 * There are two classes of instructions: The first class we track both
	 * alu32 and alu64 sign/unsigned bounds independently this provides the
	 * greatest amount of precision when alu operations are mixed with jmp32
	 * operations. These operations are BPF_ADD, BPF_SUB, BPF_MUL, BPF_ADD,
	 * and BPF_OR. This is possible because these ops have fairly easy to
	 * understand and calculate behavior in both 32-bit and 64-bit alu ops.
	 * See alu32 verifier tests for examples. The second class of
	 * operations, BPF_LSH, BPF_RSH, and BPF_ARSH, however are not so easy
	 * with regards to tracking sign/unsigned bounds because the bits may
	 * cross subreg boundaries in the alu64 case. When this happens we mark
	 * the reg unbounded in the subreg bound space and use the resulting
	 * tnum to calculate an approximation of the sign/unsigned bounds.
	 */
	switch (opcode) {
	case BPF_ADD:
		scalar32_min_max_add(dst_reg, &src_reg);
		scalar_min_max_add(dst_reg, &src_reg);
		dst_reg->var_off = tnum_add(dst_reg->var_off, src_reg.var_off);
		break;
	case BPF_SUB:
		scalar32_min_max_sub(dst_reg, &src_reg);
		scalar_min_max_sub(dst_reg, &src_reg);
		dst_reg->var_off = tnum_sub(dst_reg->var_off, src_reg.var_off);
		break;
	case BPF_MUL:
		dst_reg->var_off = tnum_mul(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_mul(dst_reg, &src_reg);
		scalar_min_max_mul(dst_reg, &src_reg);
		break;
	case BPF_AND:
		dst_reg->var_off = tnum_and(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_and(dst_reg, &src_reg);
		scalar_min_max_and(dst_reg, &src_reg);
		break;
	case BPF_OR:
		dst_reg->var_off = tnum_or(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_or(dst_reg, &src_reg);
		scalar_min_max_or(dst_reg, &src_reg);
		break;
	case BPF_XOR:
		dst_reg->var_off = tnum_xor(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_xor(dst_reg, &src_reg);
		scalar_min_max_xor(dst_reg, &src_reg);
		break;
	case BPF_LSH:
		if (umax_val >= insn_bitness) {
			/* Shifts greater than 31 or 63 are undefined.
			 * This includes shifts by a negative number.
			 */
			mark_reg_unknown(env, regs, insn->dst_reg);
			break;
		}
		if (alu32)
			scalar32_min_max_lsh(dst_reg, &src_reg);
		else
			scalar_min_max_lsh(dst_reg, &src_reg);
		break;
	case BPF_RSH:
		if (umax_val >= insn_bitness) {
			/* Shifts greater than 31 or 63 are undefined.
			 * This includes shifts by a negative number.
			 */
			mark_reg_unknown(env, regs, insn->dst_reg);
			break;
		}
		if (alu32)
			scalar32_min_max_rsh(dst_reg, &src_reg);
		else
			scalar_min_max_rsh(dst_reg, &src_reg);
		break;
	case BPF_ARSH:
		if (umax_val >= insn_bitness) {
			/* Shifts greater than 31 or 63 are undefined.
			 * This includes shifts by a negative number.
			 */
			mark_reg_unknown(env, regs, insn->dst_reg);
			break;
		}
		if (alu32)
			scalar32_min_max_arsh(dst_reg, &src_reg);
		else
			scalar_min_max_arsh(dst_reg, &src_reg);
		break;
	default:
		mark_reg_unknown(env, regs, insn->dst_reg);
		break;
	}

	/* ALU32 ops are zero extended into 64bit register */
	if (alu32)
		zext_32_to_64(dst_reg);
	reg_bounds_sync(dst_reg);
	return 0;
}
```

Why it is not vulnerable: The function consistently calls reg_bounds_sync(dst_reg) at the end of the normal execution path, after all switch-case operations that modify dst_reg->var_off and scalar bounds. This ensures that after any operation refines the variable offset (tnum), the min/max values are synchronized to remain consistent. Early return paths either mark the register as unknown (a terminal state that doesn't require further bound tracking) or return an error from sanitization. The reg_bounds_sync function performs a multi-pass reconciliation: it updates bounds from var_off, deduces sign bit information, propagates bit knowledge to var_off, and performs a final bounds update.

Security invariants:
- After modifying dst_reg->var_off in any switch case, bounds must be synchronized to ensure min/max values remain consistent with the tnum state. Enforced by the unconditional reg_bounds_sync(dst_reg) call at the end of the function before the normal return.
- Register marked as unknown represents a terminal unknown state; bounds need not be further refined. Enforced by __mark_reg_unknown marking the register unknown and returning early, and by mark_reg_unknown in shift overflow cases and default case.
- Shift operations must not allow undefined shift amounts (>= bitness). Enforced by the umax_val >= insn_bitness checks in BPF_LSH, BPF_RSH, and BPF_ARSH cases, which mark the register unknown when violated.
- Source register bounds must be consistent (smin <= smax, umin <= umax) and match constancy expectations. Enforced by the bounds validation checks at the start that mark dst unknown if inconsistencies exist.

Invariant verification:
- Bounds synchronization after var_off refinement: holds=true. Evidence: Every switch case that modifies dst_reg->var_off (ADD, SUB, MUL, AND, OR, XOR, and the non-overflow paths of LSH/RSH/ARSH) falls through to reg_bounds_sync(dst_reg) at the end of the function before return 0. Cases that cannot compute bounds (shift overflow, default opcode) call mark_reg_unknown and break, reaching reg_bounds_sync which handles the unknown state.
- Early return paths do not leave inconsistent partial state: holds=true. Evidence: The two early return paths (invalid source bounds check and sanitize error) both occur before any modification to dst_reg's bound fields. The invalid bounds path calls __mark_reg_unknown(env, dst_reg) to transition dst to unknown state before returning. The sanitize path returns an error code preventing continued execution.
- ALU32 zero-extension applied before synchronization: holds=true. Evidence: The if (alu32) zext_32_to_64(dst_reg) call occurs after the switch statement and before reg_bounds_sync(dst_reg), ensuring zero-extension effects are incorporated before bounds reconciliation.

Residual risk notes: The correctness of bounds synchronization depends on the internal correctness of scalar_min_max_* functions and tnum_* operations. Additionally, when mark_reg_unknown is called for shift overflow, the function still proceeds to reg_bounds_sync on the now-unknown register, which is benign but represents redundant computation. The function trusts that sanitize_val_alu properly handles its designated sanitization responsibilities.

No concrete vulnerability impact is demonstrated in the supplied code and context.

Control flow: The function extracts scalar bounds from src_reg, validates consistency (min <= max for signed/unsigned), marks dst_reg unknown and returns early if bounds are invalid. For non-constant sources with operations other than ADD/SUB/AND, it marks dst_reg unknown. It calls sanitize_val_alu if needed, then dispatches to operation-specific bound calculation via a switch on opcode. For shift operations (LSH/RSH/ARSH), it checks if umax_val >= insn_bitness before proceeding; if exceeded, it marks the register unknown and breaks. After the switch, alu32 operations are zero-extended, and reg_bounds_sync is called to synchronize bounds before returning 0.

Data flow: Input src_reg bounds (smin/smax/umin/umax, s32/u32 variants) are copied to local variables. For shift operations, umax_val (unsigned max of shift amount) is compared against insn_bitness (32 or 64). If valid, operation-specific functions (scalar32_min_max_lsh, scalar_min_max_lsh, etc.) compute new dst_reg bounds. The var_off tnum is updated via tnum operations. Output is the modified dst_reg with synchronized bounds.

Verdict: Not Vulnerable

Reviewed region:
[kernel/bpf/verifier.c]
```c
static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
				      struct bpf_insn *insn,
				      struct bpf_reg_state *dst_reg,
				      struct bpf_reg_state src_reg)
{
	struct bpf_reg_state *regs = cur_regs(env);
	u8 opcode = BPF_OP(insn->code);
	bool src_known;
	s64 smin_val, smax_val;
	u64 umin_val, umax_val;
	s32 s32_min_val, s32_max_val;
	u32 u32_min_val, u32_max_val;
	u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
	bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64);
	int ret;

	smin_val = src_reg.smin_value;
	smax_val = src_reg.smax_value;
	umin_val = src_reg.umin_value;
	umax_val = src_reg.umax_value;

	s32_min_val = src_reg.s32_min_value;
	s32_max_val = src_reg.s32_max_value;
	u32_min_val = src_reg.u32_min_value;
	u32_max_val = src_reg.u32_max_value;

	if (alu32) {
		src_known = tnum_subreg_is_const(src_reg.var_off);
		if ((src_known &&
		     (s32_min_val != s32_max_val || u32_min_val != u32_max_val)) ||
		    s32_min_val > s32_max_val || u32_min_val > u32_max_val) {
			/* Taint dst register if offset had invalid bounds
			 * derived from e.g. dead branches.
			 */
			__mark_reg_unknown(env, dst_reg);
			return 0;
		}
	} else {
		src_known = tnum_is_const(src_reg.var_off);
		if ((src_known &&
		     (smin_val != smax_val || umin_val != umax_val)) ||
		    smin_val > smax_val || umin_val > umax_val) {
			/* Taint dst register if offset had invalid bounds
			 * derived from e.g. dead branches.
			 */
			__mark_reg_unknown(env, dst_reg);
			return 0;
		}
	}

	if (!src_known &&
	    opcode != BPF_ADD && opcode != BPF_SUB && opcode != BPF_AND) {
		__mark_reg_unknown(env, dst_reg);
		return 0;
	}

	if (sanitize_needed(opcode)) {
		ret = sanitize_val_alu(env, insn);
		if (ret < 0)
			return sanitize_err(env, insn, ret, NULL, NULL);
	}

	/* Calculate sign/unsigned bounds and tnum for alu32 and alu64 bit ops.
	 * There are two classes of instructions: The first class we track both
	 * alu32 and alu64 sign/unsigned bounds independently this provides the
	 * greatest amount of precision when alu operations are mixed with jmp32
	 * operations. These operations are BPF_ADD, BPF_SUB, BPF_MUL, BPF_ADD,
	 * and BPF_OR. This is possible because these ops have fairly easy to
	 * understand and calculate behavior in both 32-bit and 64-bit alu ops.
	 * See alu32 verifier tests for examples. The second class of
	 * operations, BPF_LSH, BPF_RSH, and BPF_ARSH, however are not so easy
	 * with regards to tracking sign/unsigned bounds because the bits may
	 * cross subreg boundaries in the alu64 case. When this happens we mark
	 * the reg unbounded in the subreg bound space and use the resulting
	 * tnum to calculate an approximation of the sign/unsigned bounds.
	 */
	switch (opcode) {
	case BPF_ADD:
		scalar32_min_max_add(dst_reg, &src_reg);
		scalar_min_max_add(dst_reg, &src_reg);
		dst_reg->var_off = tnum_add(dst_reg->var_off, src_reg.var_off);
		break;
	case BPF_SUB:
		scalar32_min_max_sub(dst_reg, &src_reg);
		scalar_min_max_sub(dst_reg, &src_reg);
		dst_reg->var_off = tnum_sub(dst_reg->var_off, src_reg.var_off);
		break;
	case BPF_MUL:
		dst_reg->var_off = tnum_mul(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_mul(dst_reg, &src_reg);
		scalar_min_max_mul(dst_reg, &src_reg);
		break;
	case BPF_AND:
		dst_reg->var_off = tnum_and(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_and(dst_reg, &src_reg);
		scalar_min_max_and(dst_reg, &src_reg);
		break;
	case BPF_OR:
		dst_reg->var_off = tnum_or(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_or(dst_reg, &src_reg);
		scalar_min_max_or(dst_reg, &src_reg);
		break;
	case BPF_XOR:
		dst_reg->var_off = tnum_xor(dst_reg->var_off, src_reg.var_off);
		scalar32_min_max_xor(dst_reg, &src_reg);
		scalar_min_max_xor(dst_reg, &src_reg);
		break;
	case BPF_LSH:
		if (umax_val >= insn_bitness) {
			/* Shifts greater than 31 or 63 are undefined.
			 * This includes shifts by a negative number.
			 */
			mark_reg_unknown(env, regs, insn->dst_reg);
			break;
		}
		if (alu32)
			scalar32_min_max_lsh(dst_reg, &src_reg);
		else
			scalar_min_max_lsh(dst_reg, &src_reg);
		break;
	case BPF_RSH:
		if (umax_val >= insn_bitness) {
			/* Shifts greater than 31 or 63 are undefined.
			 * This includes shifts by a negative number.
			 */
			mark_reg_unknown(env, regs, insn->dst_reg);
			break;
		}
		if (alu32)
			scalar32_min_max_rsh(dst_reg, &src_reg);
		else
			scalar_min_max_rsh(dst_reg, &src_reg);
		break;
	case BPF_ARSH:
		if (umax_val >= insn_bitness) {
			/* Shifts greater than 31 or 63 are undefined.
			 * This includes shifts by a negative number.
			 */
			mark_reg_unknown(env, regs, insn->dst_reg);
			break;
		}
		if (alu32)
			scalar32_min_max_arsh(dst_reg, &src_reg);
		else
			scalar_min_max_arsh(dst_reg, &src_reg);
		break;
	default:
		mark_reg_unknown(env, regs, insn->dst_reg);
		break;
	}

	/* ALU32 ops are zero extended into 64bit register */
	if (alu32)
		zext_32_to_64(dst_reg);
	reg_bounds_sync(dst_reg);
	return 0;
}
```

Why it is not vulnerable: The shift bounds validation at `if (umax_val >= insn_bitness)` for BPF_LSH, BPF_RSH, and BPF_ARSH correctly rejects undefined shift amounts. This check catches both shifts exceeding the bit width (e.g., shift by 64+) and negative shift amounts, because any negative signed value has a very large unsigned representation (e.g., -1 as u64 is 18446744073709551615) that exceeds insn_bitness (32 or 64). The initial bounds validation ensures smin <= smax and umin <= umax, and reg_bounds_sync at the end maintains bound consistency. When bounds are invalid or shifts are out of range, the register is safely marked unknown via mark_reg_unknown.

Security invariants:
- Shift amount must be in range [0, insn_bitness-1] for defined behavior; enforced by `if (umax_val >= insn_bitness)` check before shift operations, which catches both over-large and negative shift amounts since negative values have large unsigned representations
- Register bounds must be consistent (min <= max) before processing; enforced by the initial validation checks `s32_min_val > s32_max_val || u32_min_val > u32_max_val` (alu32) and `smin_val > smax_val || umin_val > umax_val` (alu64)
- Constant source registers must have matching signed/unsigned bounds; enforced by checks `src_known && (s32_min_val != s32_max_val || u32_min_val != u32_max_val)` and equivalent for alu64
- Unknown or invalid operations must result in unknown register state; enforced by mark_reg_unknown calls in the default switch case and when bounds are invalid
- Bounds must be synchronized after modification; enforced by the reg_bounds_sync(dst_reg) call at function exit

Invariant verification:
- Shift amount upper bound validation: holds=true. Evidence: The check `if (umax_val >= insn_bitness)` before scalar32_min_max_lsh/scalar_min_max_lsh (and equivalent for RSH/ARSH) ensures shifts by amounts >= 32 or >= 64 are rejected. Negative shift amounts are caught because their unsigned representation (e.g., -1 as u64 = 18446744073709551615) exceeds insn_bitness.
- Register bounds consistency validation: holds=true. Evidence: Initial checks `smin_val > smax_val || umin_val > umax_val` (alu64) and `s32_min_val > s32_max_val || u32_min_val > u32_max_val` (alu32) reject registers with inverted bounds, marking dst_reg unknown.
- Post-operation bound synchronization: holds=true. Evidence: The call `reg_bounds_sync(dst_reg)` at the end of the function invokes __update_reg_bounds, __reg_deduce_bounds, and __reg_bound_offset to maintain consistency between var_off and min/max bounds.
- Fallback to unknown state for unsupported operations: holds=true. Evidence: The `default` case in the switch statement calls `mark_reg_unknown(env, regs, insn->dst_reg)`, ensuring no operation is processed without explicit bound calculation logic.

Residual risk notes: The shift bounds check relies on the invariant that the verifier's bound tracking always produces umax values that accurately reflect the maximum possible unsigned value of a register. If a register could somehow have a negative signed value while reporting a small unsigned max (which would require a bug elsewhere in the verifier's bound tracking), the check could be bypassed. However, based on the supplied context showing reg_bounds_sync and the bound deduction functions, the verifier appears to maintain this invariant through cross-synchronization of signed bounds, unsigned bounds, and tnum representations.

No concrete vulnerability impact identified


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: drivers/soc/imx/soc-imx8m.c
Function: imx8_soc_init

Call path: imx8_soc_init (drivers/soc/imx/soc-imx8m.c) → imx8mm_soc_revision (drivers/soc/imx/soc-imx8m.c) → of_clk_get_by_name (drivers/clk/clk.c)

### Primary Function

```c
static int imx8m_soc_probe(struct platform_device *pdev)
{
	struct soc_device_attribute *soc_dev_attr;
	struct soc_device *soc_dev;
	const struct of_device_id *id;
	u32 soc_rev = 0;
	const struct imx8_soc_data *data;
	int ret;

	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
	if (!soc_dev_attr)
		return -ENOMEM;

	soc_dev_attr->family = "Freescale i.MX";

	ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
	if (ret)
		goto free_soc;

	id = of_match_node(imx8_soc_match, of_root);
	if (!id) {
		ret = -ENODEV;
		goto free_soc;
	}

	data = id->data;
	if (data) {
		soc_dev_attr->soc_id = data->name;
		if (data->soc_revision) {
			ret = data->soc_revision(&soc_rev);
			if (ret)
				goto free_soc;
		}
	}

	soc_dev_attr->revision = imx8_revision(soc_rev);
	if (!soc_dev_attr->revision) {
		ret = -ENOMEM;
		goto free_soc;
	}

	soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", soc_uid);
	if (!soc_dev_attr->serial_number) {
		ret = -ENOMEM;
		goto free_rev;
	}

	soc_dev = soc_device_register(soc_dev_attr);
	if (IS_ERR(soc_dev)) {
		ret = PTR_ERR(soc_dev);
		goto free_serial_number;
	}

	pr_info("SoC: %s revision %s\n", soc_dev_attr->soc_id,
		soc_dev_attr->revision);

	if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT))
		platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);

	return 0;

free_serial_number:
	kfree(soc_dev_attr->serial_number);
free_rev:
	if (strcmp(soc_dev_attr->revision, "unknown"))
		kfree(soc_dev_attr->revision);
free_soc:
	kfree(soc_dev_attr);
	return ret;
}
```

### Cross-File Context

[imx8_soc_data — struct — drivers/soc/imx/soc-imx8m.c:30-33]
```c
struct imx8_soc_data {
	char *name;
	int (*soc_revision)(u32 *socrev);
};
```

[imx8mm_soc_revision — callee — drivers/soc/imx/soc-imx8m.c:154-176]
```c
static int imx8mm_soc_revision(u32 *socrev)
{
	struct device_node *np;
	void __iomem *anatop_base;
	int ret;

	np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop");
	if (!np)
		return -EINVAL;

	anatop_base = of_iomap(np, 0);
	if (!anatop_base) {
		ret = -EINVAL;
		goto err_iomap;
	}

	*socrev = readl_relaxed(anatop_base + ANADIG_DIGPROG_IMX8MM);

	iounmap(anatop_base);
	of_node_put(np);

	return imx8mm_soc_uid();

err_iomap:
	of_node_put(np);
	return ret;
}
```

[imx8mq_soc_revision — function — drivers/soc/imx/soc-imx8m.c:54-100]
```c
static int imx8mq_soc_revision(u32 *socrev)
{
	struct device_node *np;
	void __iomem *ocotp_base;
	u32 magic;
	u32 rev;
	struct clk *clk;
	int ret;

	np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp");
	if (!np)
		return -EINVAL;

	ocotp_base = of_iomap(np, 0);
	if (!ocotp_base) {
		ret = -EINVAL;
		goto err_iomap;
	}

	clk = of_clk_get_by_name(np, NULL);
	if (IS_ERR(clk)) {
		ret = PTR_ERR(clk);
		goto err_clk;
	}

	clk_prepare_enable(clk);

	/*
	 * SOC revision on older imx8mq is not available in fuses so query
	 * the value from ATF instead.
	 */
	rev = imx8mq_soc_revision_from_atf();
	if (!rev) {
		magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1);
		if (magic == IMX8MQ_SW_MAGIC_B1)
			rev = REV_B1;
	}

	soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH);
	soc_uid <<= 32;
	soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW);

	*socrev = rev;

	clk_disable_unprepare(clk);
	clk_put(clk);
	iounmap(ocotp_base);
	of_node_put(np);

	return 0;

err_clk:
	iounmap(ocotp_base);
err_iomap:
	of_node_put(np);
	return ret;
}
```

[imx8mm_soc_uid — function — drivers/soc/imx/soc-imx8m.c:117-152]
```c
static int imx8mm_soc_uid(void)
{
	void __iomem *ocotp_base;
	struct device_node *np;
	struct clk *clk;
	int ret = 0;
	u32 offset = of_machine_is_compatible("fsl,imx8mp") ?
		     IMX8MP_OCOTP_UID_OFFSET : 0;

	np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-ocotp");
	if (!np)
		return -EINVAL;

	ocotp_base = of_iomap(np, 0);
	if (!ocotp_base) {
		ret = -EINVAL;
		goto err_iomap;
	}

	clk = of_clk_get_by_name(np, NULL);
	if (IS_ERR(clk)) {
		ret = PTR_ERR(clk);
		goto err_clk;
	}

	clk_prepare_enable(clk);

	soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH + offset);
	soc_uid <<= 32;
	soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW + offset);

	clk_disable_unprepare(clk);
	clk_put(clk);

err_clk:
	iounmap(ocotp_base);
err_iomap:
	of_node_put(np);

	return ret;
}
```

[imx8_revision — macro — drivers/soc/imx/soc-imx8m.c:184-187]
imx8_revision → #define imx8_revision(soc_rev) \ soc_rev ? \ kasprintf(GFP_KERNEL, "%d.%d", (soc_rev >> 4) & 0xf, soc_rev & 0xf) : \ "unknown"  (drivers/soc/imx/soc-imx8m.c:184-187)

[imx8m_soc_driver — struct — drivers/soc/imx/soc-imx8m.c:288-293]
```c
static struct platform_driver imx8m_soc_driver = {
	.probe = imx8m_soc_probe,
	.driver = {
		.name = "imx8m-soc",
	},
};
```

Control flow: The probe function begins by allocating a `soc_device_attribute` structure and validating the pointer. It reads the device model string and matches the device tree node to retrieve driver-specific data. If `data` is present, it checks for an optional `soc_revision` callback. If the callback exists, it is invoked with a pointer to `soc_rev`. The return value is immediately validated; a non-zero result triggers a jump to `free_soc`. On success, the function allocates memory for the revision string and serial number, validates both allocations, registers the SOC device, and returns 0. Error paths jump to labeled cleanup sections that free resources in reverse allocation order, carefully skipping labels for resources not yet allocated.

Data flow: `soc_rev` is initialized to 0 and passed by reference to the `soc_revision` callback. The callback reads hardware registers to populate `soc_rev` and a global `soc_uid` variable. `soc_rev` is then passed to the `imx8_revision` macro, which formats it into a heap-allocated string or returns a static "unknown" literal. `soc_uid` is formatted into a serial number string. All dynamically allocated strings and the main attribute structure are tracked and freed during error handling or module unload. Pointer returns from `kasprintf` and `soc_device_register` are validated against NULL and IS_ERR respectively.

Verdict: Not Vulnerable

Reviewed region:
[drivers/soc/imx/soc-imx8m.c]
```c
static int imx8m_soc_probe(struct platform_device *pdev)
{
	struct soc_device_attribute *soc_dev_attr;
	struct soc_device *soc_dev;
	const struct of_device_id *id;
	u32 soc_rev = 0;
	const struct imx8_soc_data *data;
	int ret;

	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
	if (!soc_dev_attr)
		return -ENOMEM;

	soc_dev_attr->family = "Freescale i.MX";

	ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
	if (ret)
		goto free_soc;

	id = of_match_node(imx8_soc_match, of_root);
	if (!id) {
		ret = -ENODEV;
		goto free_soc;
	}

	data = id->data;
	if (data) {
		soc_dev_attr->soc_id = data->name;
		if (data->soc_revision) {
			ret = data->soc_revision(&soc_rev);
			if (ret)
				goto free_soc;
		}
	}

	soc_dev_attr->revision = imx8_revision(soc_rev);
	if (!soc_dev_attr->revision) {
		ret = -ENOMEM;
		goto free_soc;
	}

	soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", soc_uid);
	if (!soc_dev_attr->serial_number) {
		ret = -ENOMEM;
		goto free_rev;
	}

	soc_dev = soc_device_register(soc_dev_attr);
	if (IS_ERR(soc_dev)) {
		ret = PTR_ERR(soc_dev);
		goto free_serial_number;
	}

	pr_info("SoC: %s revision %s\n", soc_dev_attr->soc_id,
		soc_dev_attr->revision);

	if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT))
		platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);

	return 0;

free_serial_number:
	kfree(soc_dev_attr->serial_number);
free_rev:
	if (strcmp(soc_dev_attr->revision, "unknown"))
		kfree(soc_dev_attr->revision);
free_soc:
	kfree(soc_dev_attr);
	return ret;
}
```

Why it is not vulnerable: The optional `data->soc_revision` callback is explicitly guarded by a null pointer check before invocation. Upon invocation, its return value is stored in `ret` and immediately validated with `if (ret) goto free_soc;`. This ensures that any error condition encountered by the callback is correctly propagated, halting further initialization and directing control to the appropriate cleanup path. No return value is silently ignored.

Security invariants:
- Optional callback pointers must be verified for non-NULL status before dereference.
- Return values from invoked callbacks must be checked to differentiate success from failure.
- Error handling paths must correctly skip cleanup steps for resources that have not yet been allocated.
- Static string literals returned by macros must not be passed to kfree.

Invariant verification:
- Callback null check: holds=true. Evidence: The code wraps the invocation in `if (data->soc_revision) { ... }`, preventing NULL pointer dereference if the callback is not defined for a specific SoC variant.
- Return value validation: holds=true. Evidence: The assignment `ret = data->soc_revision(&soc_rev);` is immediately followed by `if (ret) goto free_soc;`, ensuring non-zero error codes trigger error propagation.
- Cleanup path safety: holds=true. Evidence: The `goto free_soc;` on callback error bypasses `free_serial_number` and `free_rev` labels, which is correct because `soc_dev_attr->serial_number` and `soc_dev_attr->revision` are allocated after the callback call.
- Static string protection: holds=true. Evidence: The `free_rev` label uses `if (strcmp(soc_dev_attr->revision, "unknown")) kfree(soc_dev_attr->revision);`, correctly preventing deallocation of the static literal returned by the `imx8_revision` macro when `soc_rev` is 0.

Residual risk notes: No latent concerns identified. The code correctly manages the lifecycle of dynamically allocated resources, validates all critical pointers and return values, and safely handles optional driver data callbacks.

Control flow: The function imx8m_soc_probe follows a linear initialization path with multiple error exit points. On success, it allocates soc_dev_attr, reads device tree properties, matches the device, optionally calls a soc_revision callback (imx8mm_soc_revision or imx8mq_soc_revision), formats revision string, allocates serial number, and registers the SOC device. Seven distinct error/cleanup labels exist: free_serial_number, free_rev, and free_soc form a fall-through chain, while early failures jump directly to free_soc. The probe function returns 0 on success or a negative error code on failure.

Data flow: Inputs include platform_device pointer, device tree properties (model, compatible strings), and hardware registers accessed via ioremap. The soc_revision callback reads hardware revision from memory-mapped registers (ANADIG_DIGPROG_IMX8MM for i.MX8MM, OCOTP for i.MX8MQ). The soc_uid is read from OCOTP UID registers. Outputs include a registered soc_device with family, machine, soc_id, revision string, and serial number attributes.

Verdict: Not Vulnerable

Reviewed region:
[drivers/soc/imx/soc-imx8m.c]
```c
static int imx8m_soc_probe(struct platform_device *pdev)
{
	struct soc_device_attribute *soc_dev_attr;
	struct soc_device *soc_dev;
	const struct of_device_id *id;
	u32 soc_rev = 0;
	const struct imx8_soc_data *data;
	int ret;

	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
	if (!soc_dev_attr)
		return -ENOMEM;

	soc_dev_attr->family = "Freescale i.MX";

	ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
	if (ret)
		goto free_soc;

	id = of_match_node(imx8_soc_match, of_root);
	if (!id) {
		ret = -ENODEV;
		goto free_soc;
	}

	data = id->data;
	if (data) {
		soc_dev_attr->soc_id = data->name;
		if (data->soc_revision) {
			ret = data->soc_revision(&soc_rev);
			if (ret)
				goto free_soc;
		}
	}

	soc_dev_attr->revision = imx8_revision(soc_rev);
	if (!soc_dev_attr->revision) {
		ret = -ENOMEM;
		goto free_soc;
	}

	soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", soc_uid);
	if (!soc_dev_attr->serial_number) {
		ret = -ENOMEM;
		goto free_rev;
	}

	soc_dev = soc_device_register(soc_dev_attr);
	if (IS_ERR(soc_dev)) {
		ret = PTR_ERR(soc_dev);
		goto free_serial_number;
	}

	pr_info("SoC: %s revision %s\n", soc_dev_attr->soc_id,
		soc_dev_attr->revision);

	if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT))
		platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);

	return 0;

free_serial_number:
	kfree(soc_dev_attr->serial_number);
free_rev:
	if (strcmp(soc_dev_attr->revision, "unknown"))
		kfree(soc_dev_attr->revision);
free_soc:
	kfree(soc_dev_attr);
	return ret;
}
```

Why it is not vulnerable: All dynamically allocated resources are properly freed on every error path. The fall-through cleanup chain (free_serial_number → free_rev → free_soc) correctly releases resources in reverse allocation order: serial_number is freed first, then revision (with a strcmp guard to avoid freeing the "unknown" literal), then soc_dev_attr. Early error paths that jump directly to free_soc occur before any additional allocations, so only soc_dev_attr needs cleanup. The soc_revision callbacks (imx8mm_soc_revision, imx8mq_soc_revision, imx8mm_soc_uid) each properly release their own resources (of_node_put, iounmap, clk_put, clk_disable_unprepare) on all return paths before returning to the probe function.

Security invariants:
- soc_dev_attr is always freed: enforced by free_soc label (kfree(soc_dev_attr)) which is reached from all error paths
- soc_dev_attr->serial_number is freed before soc_dev_attr: enforced by free_serial_number label (kfree(soc_dev_attr->serial_number)) with fall-through to free_rev and free_soc
- soc_dev_attr->revision is freed only when dynamically allocated: enforced by strcmp check in free_rev (if (strcmp(soc_dev_attr->revision, "unknown")) kfree(...)) which distinguishes kasprintf'd strings from the "unknown" literal
- free_rev is only reached after revision is set: the control flow ensures imx8_revision() is called before any path reaching free_rev
- soc_revision callbacks release all acquired resources: imx8mm_soc_revision calls of_node_put and iounmap on all paths; imx8mq_soc_revision and imx8mm_soc_uid call of_node_put, iounmap, clk_put, and clk_disable_unprepare on all paths
- kasprintf allocation failure is handled: imx8_revision() can return NULL if kasprintf fails, caught by if (!soc_dev_attr->revision) check

Invariant verification:
- Cleanup chain order matches allocation order reversed: holds=true. Evidence: free_serial_number frees serial_number then falls to free_rev; free_rev frees revision then falls to free_soc; free_soc frees soc_dev_attr. This matches reverse allocation order: soc_dev_attr → revision → serial_number
- No use-after-free in cleanup: holds=true. Evidence: Each label frees only its designated resource and falls through; no resource is freed twice or accessed after free
- strcmp on revision is safe from NULL dereference: holds=true. Evidence: free_rev is only reachable from free_serial_number, which is only reachable after soc_dev_attr->revision = imx8_revision(soc_rev) has executed. imx8_revision returns either a kasprintf'd string or the literal "unknown", never NULL when the check passes
- Callee resource cleanup completeness: holds=true. Evidence: imx8mm_soc_revision: of_node_put(np) on all paths, iounmap(anatop_base) on success; imx8mq_soc_revision: of_node_put(np), iounmap(ocotp_base), clk_put(clk), clk_disable_unprepare(clk) on all paths; imx8mm_soc_uid: same pattern with proper error label fall-throughs
- Early error paths clean up allocated resources: holds=true. Evidence: Paths jumping to free_soc (of_property_read_string failure, of_match_node failure, soc_revision failure, revision NULL) occur before serial_number allocation, so only soc_dev_attr needs cleanup, which free_soc provides

Residual risk notes: The code relies on correct behavior of external functions (of_property_read_string, of_match_node, soc_device_register) which are not fully inspectable in the provided context. The imx8_revision macro uses kasprintf which could theoretically fail under memory pressure, but this is properly handled. No unverified assumptions about the imx8_soc_match table or platform device lifecycle were identified as problematic.


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: drivers/nvmem/core.c
Function: nvmem_shift_read_buffer_in_place

Call path: nvmem_cell_read_variable_le_u32 (drivers/nvmem/core.c) → nvmem_cell_read_variable_common (drivers/nvmem/core.c) → nvmem_cell_read (drivers/nvmem/core.c) → __nvmem_cell_read (drivers/nvmem/core.c) → nvmem_shift_read_buffer_in_place (drivers/nvmem/core.c)

### Primary Function

```c
static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
{
	u8 *p, *b;
	int i, extra, bit_offset = cell->bit_offset;

	p = b = buf;
	if (bit_offset) {
		/* First shift */
		*b++ >>= bit_offset;

		/* setup rest of the bytes if any */
		for (i = 1; i < cell->bytes; i++) {
			/* Get bits from next byte and shift them towards msb */
			*p |= *b << (BITS_PER_BYTE - bit_offset);

			p = b;
			*b++ >>= bit_offset;
		}
	} else {
		/* point to the msb */
		p += cell->bytes - 1;
	}

	/* result fits in less bytes */
	extra = cell->bytes - DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE);
	while (--extra >= 0)
		*p-- = 0;

	/* clear msb bits if any leftover in the last byte */
	if (cell->nbits % BITS_PER_BYTE)
		*p &= GENMASK((cell->nbits % BITS_PER_BYTE) - 1, 0);
}
```

### Cross-File Context

[GENMASK — macro — include/linux/kernel.h]
GENMASK → #define GENMASK(h, l) \ (((~0UL) << (l)) & (~0UL >> (sizeof(unsigned long) * BITS_PER_BYTE - 1 - (h))))  (include/linux/kernel.h)

[BITS_PER_BYTE — constant — include/linux/const.h]
BITS_PER_BYTE → 8  (include/linux/const.h)

[struct nvmem_cell — struct — drivers/nvmem/core.c:22]
```c
struct nvmem_cell {
	const char		*name;
	int			offset;
	int			bytes;
	int			bit_offset;
	int			nbits;
	struct device_node	*np;
	struct nvmem_device	*nvmem;
	struct list_head	node;
}
```

[__nvmem_cell_read — caller — drivers/nvmem/core.c:959-979]
```c
static int __nvmem_cell_read(struct nvmem_device *nvmem,
			      struct nvmem_cell *cell,
			      void *buf, size_t *len)
{
	int rc;

	rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes);

	if (rc)
		return rc;

	/* shift bits in-place */
	if (cell->bit_offset || cell->nbits)
		nvmem_shift_read_buffer_in_place(cell, buf);

	if (len)
		*len = cell->bytes;

	return 0;
}
```

[nvmem_cell_info — struct — include/linux/nvmem-consumer.h]
```c
struct nvmem_cell_info {
	const char		*name;
	unsigned int		offset;
	unsigned int		bytes;
	unsigned int		bit_offset;
	unsigned int		nbits;
};
```

Control flow: The function performs two conditional paths. If bit_offset is non-zero, it shifts bytes right by bit_offset and merges adjacent bytes using left shift. If bit_offset is zero, it advances pointer p to the last byte. Then it zeroes extra bytes at the end of the buffer (calculated as bytes needed minus bytes actually used by nbits). Finally, if nbits is not byte-aligned, it masks the highest-order bits in the final byte. The function always completes without error returns.

Data flow: Input: cell struct fields (bit_offset, bytes, nbits) and buf pointer. The buffer is transformed in-place through bitwise operations: right shifts by bit_offset, left shifts by (8 - bit_offset), OR operations to merge adjacent bytes, zeroing of unused trailing bytes, and masking of leftover high-order bits. The GENMASK macro generates a bitmask using the remainder of nbits divided by BITS_PER_BYTE.

Verdict: Not Vulnerable

Reviewed region:
[drivers/nvmem/core.c]
```c
static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
{
	u8 *p, *b;
	int i, extra, bit_offset = cell->bit_offset;

	p = b = buf;
	if (bit_offset) {
		/* First shift */
		*b++ >>= bit_offset;

		/* setup rest of the bytes if any */
		for (i = 1; i < cell->bytes; i++) {
			/* Get bits from next byte and shift them towards msb */
			*p |= *b << (BITS_PER_BYTE - bit_offset);

			p = b;
			*b++ >>= bit_offset;
		}
	} else {
		/* point to the msb */
		p += cell->bytes - 1;
	}

	/* result fits in less bytes */
	extra = cell->bytes - DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE);
	while (--extra >= 0)
		*p-- = 0;

	/* clear msb bits if any leftover in the last byte */
	if (cell->nbits % BITS_PER_BYTE)
		*p &= GENMASK((cell->nbits % BITS_PER_BYTE) - 1, 0);
}
```

Why it is not vulnerable: The shift operations use bit_offset which ranges from 0-7 (a byte offset), and the code guards the GENMASK call with 'if (cell->nbits % BITS_PER_BYTE)' which ensures the mask high-bit parameter is in range [0, 6] rather than -1. All shift amounts in GENMASK are within valid bounds: l=0 is safe, and the right shift amount is sizeof(unsigned long)*BITS_PER_BYTE-1-h where h is at most 6, yielding at minimum 58 on a 64-bit system. The buffer access patterns are bounded by cell->bytes which matches the allocation size from the caller.

Security invariants:
- bit_offset must be in [0, 7] for safe byte-level shifts - enforced by the struct definition and the calling path which uses bit_offset as a per-byte alignment value
- The GENMASK expression must not use a negative high-bit value - enforced by the guard 'if (cell->nbits % BITS_PER_BYTE)' which only enters when nbits is not byte-aligned, ensuring (nbits % 8) is in [1, 7], so (nbits % 8 - 1) is in [0, 6]
- Shift amounts in GENMASK must not exceed the operand width - l=0 is always valid, and the right shift amount 'sizeof(unsigned long)*BITS_PER_BYTE - 1 - h' where h is in [0,6] yields values in [58, 63] on 64-bit, all valid
- Buffer pointer arithmetic must stay within allocated bounds - the extra bytes zeroing loop decrements p at most 'cell->bytes - DIV_ROUND_UP(cell->nbits, 8)' times, which is at most cell->bytes, and p is initialized to the last byte of the buffer
- The buffer must be at least cell->bytes in size - enforced by caller __nvmem_cell_read which passes the same cell->bytes size to nvmem_reg_read

Invariant verification:
- GENMASK high-bit parameter is non-negative: holds=true. Evidence: The expression 'if (cell->nbits % BITS_PER_BYTE)' gates the GENMASK call, ensuring nbits % 8 is in [1, 7], so (cell->nbits % BITS_PER_BYTE) - 1 is in [0, 6]
- Shift amounts do not exceed operand width: holds=true. Evidence: In '*b >>= bit_offset' and '*p |= *b << (BITS_PER_BYTE - bit_offset)', bit_offset is a byte alignment in [0,7]. In GENMASK, l=0 and the right shift argument is at most sizeof(unsigned long)*8-1 which equals the bit width minus 1, always safe
- Buffer access remains within bounds: holds=true. Evidence: The loop 'for (i = 1; i < cell->bytes; i++)' accesses at most cell->bytes elements. The zeroing loop 'while (--extra >= 0) *p-- = 0' decrements at most cell->bytes times from the last valid position

Residual risk notes: The code assumes cell->bytes matches the actual buffer allocation size from the caller. If nvmem_reg_read were to allocate a different size, bounds could be violated. Also, the code assumes bit_offset is strictly within [0, 7]; values outside this range would cause undefined behavior in shifts, though this appears enforced by the data model. The GENMASK macro's intermediate computation uses unsigned long arithmetic which is safe but could have different behavior on 32-bit vs 64-bit systems (affects only the temporary computation, not the final masked u8 result).

Control flow: The function first shifts bytes right by bit_offset if non-zero, combining adjacent bytes. If bit_offset is zero, pointer p advances to the last byte. Then extra bytes beyond what nbits requires are zeroed out. Finally, if nbits is not a multiple of BITS_PER_BYTE, the most significant leftover bits in the last byte are cleared using GENMASK.

Data flow: Input buf contains raw bytes from NVMEM read. The function transforms buf in-place by right-shifting by bit_offset, combining adjacent bytes, zeroing excess bytes, and masking the MSB of the final byte. The cell struct provides bit_offset, bytes, and nbits parameters that control the transformation.

Verdict: Not Vulnerable

Reviewed region:
[drivers/nvmem/core.c]
```c
static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
{
	u8 *p, *b;
	int i, extra, bit_offset = cell->bit_offset;

	p = b = buf;
	if (bit_offset) {
		/* First shift */
		*b++ >>= bit_offset;

		/* setup rest of the bytes if any */
		for (i = 1; i < cell->bytes; i++) {
			/* Get bits from next byte and shift them towards msb */
			*p |= *b << (BITS_PER_BYTE - bit_offset);

			p = b;
			*b++ >>= bit_offset;
		}
	} else {
		/* point to the msb */
		p += cell->bytes - 1;
	}

	/* result fits in less bytes */
	extra = cell->bytes - DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE);
	while (--extra >= 0)
		*p-- = 0;

	/* clear msb bits if any leftover in the last byte */
	if (cell->nbits % BITS_PER_BYTE)
		*p &= GENMASK((cell->nbits % BITS_PER_BYTE) - 1, 0);
}
```

Why it is not vulnerable: The GENMASK invocation at line `*p &= GENMASK((cell->nbits % BITS_PER_BYTE) - 1, 0)` is protected by the guard `if (cell->nbits % BITS_PER_BYTE)`. This condition ensures the body only executes when `cell->nbits % BITS_PER_BYTE` is non-zero (i.e., in range [1, 7]). Therefore, the expression `(cell->nbits % BITS_PER_BYTE) - 1` evaluates to a value in [0, 6], never -1, preventing any unsigned integer underflow in the GENMASK macro arguments. When nbits is a multiple of 8, the condition is false and the GENMASK line is skipped entirely.

Security invariants:
- The GENMASK high-bit argument must not underflow to an invalid value. Enforced by: `if (cell->nbits % BITS_PER_BYTE)` guard ensures `(cell->nbits % BITS_PER_BYTE) - 1` is in range [0, 6] when GENMASK is evaluated.
- The buffer pointer arithmetic must stay within allocated bounds. Enforced by: the loop `for (i = 1; i < cell->bytes; i++)` iterates exactly cell->bytes - 1 times after the first byte, and the extra-zeroing loop decrements from `cell->bytes - DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE)` down to 0.
- The caller ensures the function is only invoked when needed. Enforced by: `if (cell->bit_offset || cell->nbits)` check in `__nvmem_cell_read` before calling this function.

Invariant verification:
- GENMASK argument underflow prevention: holds=true. Evidence: The condition `if (cell->nbits % BITS_PER_BYTE)` at the final if-statement ensures the body only executes when the modulo is in [1,7], making `(cell->nbits % BITS_PER_BYTE) - 1` evaluate to [0,6], a valid unsigned range for GENMASK's high-bit parameter.
- Buffer pointer bounds during shift loop: holds=true. Evidence: The loop `for (i = 1; i < cell->bytes; i++)` combined with `b++` ensures exactly cell->bytes bytes are accessed, matching the buffer size from the caller's nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes) call.
- Extra byte zeroing loop bounds: holds=true. Evidence: The expression `extra = cell->bytes - DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE)` computes non-negative extra bytes, and `while (--extra >= 0)` decrements p safely within the buffer region.

Residual risk notes: The GENMASK macro uses unsigned long arithmetic, so its behavior depends on the platform's unsigned long width. However, since the high-bit argument is bounded to [0,6], this is well within safe limits on any platform. The function assumes buf is non-NULL and has at least cell->bytes allocated, which is enforced by the caller's nvmem_reg_read invocation.

No concrete vulnerability impact identified in the supplied code and context.