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/net/ethernet/marvell/octeontx2/nic/otx2_common.c
Function: otx2_sq_aura_pool_init

Call path: otx2_open (drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c) → otx2_init_hw_resources (drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c) → otx2_sq_aura_pool_init (drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c) → otx2_aura_init (drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c) → otx2_pool_init (drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c) → kcalloc (include/linux/slab.h)

### Primary Function

```c
int otx2_sq_aura_pool_init(struct otx2_nic *pfvf)
{
	int qidx, pool_id, stack_pages, num_sqbs;
	struct otx2_qset *qset = &pfvf->qset;
	struct otx2_hw *hw = &pfvf->hw;
	struct otx2_snd_queue *sq;
	struct otx2_pool *pool;
	dma_addr_t bufptr;
	int err, ptr;

	/* Calculate number of SQBs needed.
	 *
	 * For a 128byte SQE, and 4K size SQB, 31 SQEs will fit in one SQB.
	 * Last SQE is used for pointing to next SQB.
	 */
	num_sqbs = (hw->sqb_size / 128) - 1;
	num_sqbs = (qset->sqe_cnt + num_sqbs) / num_sqbs;

	/* Get no of stack pages needed */
	stack_pages =
		(num_sqbs + hw->stack_pg_ptrs - 1) / hw->stack_pg_ptrs;

	for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) {
		pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx);
		/* Initialize aura context */
		err = otx2_aura_init(pfvf, pool_id, pool_id, num_sqbs);
		if (err)
			goto fail;

		/* Initialize pool context */
		err = otx2_pool_init(pfvf, pool_id, stack_pages,
				     num_sqbs, hw->sqb_size);
		if (err)
			goto fail;
	}

	/* Flush accumulated messages */
	err = otx2_sync_mbox_msg(&pfvf->mbox);
	if (err)
		goto fail;

	get_cpu();
	/* Allocate pointers and free them to aura/pool */
	for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) {
		pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx);
		pool = &pfvf->qset.pool[pool_id];

		sq = &qset->sq[qidx];
		sq->sqb_count = 0;
		sq->sqb_ptrs = kcalloc(num_sqbs, sizeof(*sq->sqb_ptrs), GFP_KERNEL);
		if (!sq->sqb_ptrs) {
			err = -ENOMEM;
			goto err_mem;
		}

		for (ptr = 0; ptr < num_sqbs; ptr++) {
			err = otx2_alloc_rbuf(pfvf, pool, &bufptr);
			if (err)
				goto err_mem;
			pfvf->hw_ops->aura_freeptr(pfvf, pool_id, bufptr);
			sq->sqb_ptrs[sq->sqb_count++] = (u64)bufptr;
		}
	}

err_mem:
	put_cpu();
	return err ? -ENOMEM : 0;

fail:
	otx2_mbox_reset(&pfvf->mbox.mbox, 0);
	otx2_aura_pool_free(pfvf);
	return err;
}
```

### Cross-File Context

[get_cpu — macro — include/linux/preempt.h]
get_cpu → #define get_cpu() \n({ int __ret = __get_cpu_nocheck(); preempt_count_inc(); __ret; })  (include/linux/preempt.h)

[put_cpu — macro — include/linux/preempt.h]
put_cpu → #define put_cpu() \n preempt_count_dec();  (include/linux/preempt.h)

[GFP_KERNEL — constant — include/linux/gfp.h]
GFP_KERNEL → (__GFP_RECLAIM | __GFP_COMP | __GFP_MEMALLOC | __GFP_NORETRY | __GFP_NOWARN)  (include/linux/gfp.h)

[kcalloc — sink — include/linux/slab.h]
```c
static inline void *kcalloc(size_t n, size_t size, gfp_t flags)
{
	return kmalloc_array(n, size, flags | __GFP_ZERO);
}
```

[AURA_NIX_SQ — enum — drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h]
#define AURA_NIX_SQ 0x00

[otx2_nic — struct — drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h]
```c
struct otx2_nic {
	struct pci_dev	*pdev;
	struct net_device	*netdev;
	struct otx2_hw	hw;
	struct otx2_qset	qset;
	struct otx2_hw_ops	*hw_ops;
	struct dentry	*dbg_dir;
	/* ... more fields ... */
};
```

[otx2_qset — struct — drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h]
```c
struct otx2_qset {
	struct otx2_snd_queue *sq;
	struct otx2_rcv_queue *rq;
	struct otx2_cq_queue *cq;
	struct otx2_cq_poll *napi;
	int	 sqe_cnt;
	int	 rqe_cnt;
	int	 cq_cnt;
	/* ... more fields ... */
};
```

[otx2_snd_queue — struct — drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h]
```c
struct otx2_snd_queue {
	int	 sqb_count;
	u64	*sqb_ptrs;
	struct sg_list	*sg;
	/* ... more fields ... */
};
```

Control flow: otx2_open calls otx2_init_hw_resources which calls otx2_sq_aura_pool_init. Inside otx2_sq_aura_pool_init, get_cpu() disables preemption before loop, then kcalloc with GFP_KERNEL inside loop causes sleeping function warning. The fix moves get_cpu()/put_cpu() inside the loop around the aura_freeptr call only.

Data flow: pfvf (struct otx2_nic*) passed as argument, containing qset, hw, pool. num_sqbs computed from sqb_size and sqe_cnt. pool_id computed from qidx. bufptr allocated via otx2_alloc_rbuf, then freed to aura via aura_freeptr. sqb_ptrs array holds buffer pointers.

Verdict: Vulnerable
Vulnerable region:
[drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c]
```c
if (err)
		goto fail;

	get_cpu();
	/* Allocate pointers and free them to aura/pool */
	for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) {
		pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx);
		pool = &pfvf->qset.pool[pool_id];

		sq = &qset->sq[qidx];
		sq->sqb_count = 0;
		sq->sqb_ptrs = kcalloc(num_sqbs, sizeof(*sq->sqb_ptrs), GFP_KERNEL);
		if (!sq->sqb_ptrs) {
			err = -ENOMEM;
			goto err_mem;
		}

		for (ptr = 0; ptr < num_sqbs; ptr++) {
			err = otx2_alloc_rbuf(pfvf, pool, &bufptr);
			if (err)
				goto err_mem;
			pfvf->hw_ops->aura_freeptr(pfvf, pool_id, bufptr);
			sq->sqb_ptrs[sq->sqb_count++] = (u64)bufptr;
		}
	}
```
CWE: CWE-667
Severity: LOW

Root cause: The pre-fix code called get_cpu() before the queue initialization loop, acquiring a CPU in atomic context. Inside the loop, kcalloc() with GFP_KERNEL was called, which can sleep. This violates the atomic context since GFP_KERNEL allocations can block. The put_cpu() was placed after the loop at err_mem, meaning the entire loop ran with preemption disabled while potentially calling sleeping allocation functions.

Exploitability: Not exploitable for privilege escalation; causes kernel panic/warning in atomic context when driver is loaded and network interface is opened.

Attack preconditions:
- Loading octeontx2-pf driver
- Opening network device (otx2_open)
- CONFIG_DEBUG_ATOMIC_SLEEP enabled

Likely impact: Not exploitable for privilege escalation; causes kernel panic/warning in atomic context when driver is loaded and network interface is opened.

Loading octeontx2-pf driver
Opening network device (otx2_open)
CONFIG_DEBUG_ATOMIC_SLEEP enabled

The code presents a **"scheduling while atomic"** vulnerability, which is a common type of bug in Linux kernel programming.

### Analysis:

1.  **Atomic Context Entry:** The function calls `get_cpu()` at the beginning of the second `for` loop. The `get_cpu()` macro disables preemption by incrementing the preempt count (via `preempt_count_inc()`) and returns the current processor ID. This puts the execution into an atomic context.
2.  **Blocking Operation in Atomic Context:** Inside this loop, while preemption is disabled, the code calls `kcalloc`:
    ```c
    sq->sqb_ptrs = kcalloc(num_sqbs, sizeof(*sq->sqb_ptrs), GFP_KERNEL);
    ```
    The flag `GFP_KERNEL` indicates that the allocation is allowed to sleep (block) while waiting for memory to become available.
3.  **The Violation:** In the Linux kernel, functions that can sleep or block (like memory allocations with `GFP_KERNEL`) must never be called while holding a spinlock or while preemption is disabled (inside an atomic context). Doing so leads to a "scheduling while atomic" bug. If the allocator decides to sleep while preemption is disabled, the scheduler will be invoked in an invalid state, which can lead to kernel panics, deadlocks, or unpredictable behavior.
4.  **Error Path:** The `err_mem` label correctly calls `put_cpu()` to balance the preempt count, but the damage (the potential sleep while atomic) occurs at the point of the `kcalloc` call or inside `otx2_alloc_rbuf` (which likely also performs blocking operations).

### Impact:
This is a high-severity logic error/vulnerability. While it might not be directly exploitable by an external attacker to gain code execution, it can be triggered during interface initialization (e.g., when an interface is brought up via `ifconfig up` or `ip link set up`), leading to a **Kernel Panic (Denial of Service)**.

### Recommended Fix:
The memory allocation (`kcalloc`) and any other potentially sleeping operations (like `otx2_alloc_rbuf`) should be moved outside the `get_cpu()` / `put_cpu()` block. If `aura_freeptr` specifically requires preemption to be disabled (e.g., for LMTST operations), `get_cpu()` and `put_cpu()` should be scoped strictly around those specific calls, not around the entire allocation loop.

```c
// Correct approach: Move kcalloc out of the atomic section
for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) {
    sq = &qset->sq[qidx];
    sq->sqb_ptrs = kcalloc(num_sqbs, sizeof(*sq->sqb_ptrs), GFP_KERNEL);
    if (!sq->sqb_ptrs) { ... }
}

get_cpu();
// Perform hardware-specific operations that require atomic context
for (qidx = 0; qidx < hw->tot_tx_queues; qidx++) {
    // ... otx2_alloc_rbuf and aura_freeptr ...
}
put_cpu();
```

The function calls get_cpu(), which increments the preemption count and puts the execution into an atomic context. Inside this context, it calls kcalloc with the GFP_KERNEL flag. GFP_KERNEL allows the kernel to sleep if memory is not immediately available. Sleeping in an atomic context is a violation of Linux kernel concurrency primitives and results in a 'scheduling while atomic' bug.

Not directly exploitable for arbitrary code execution, but can be triggered by a local user with permissions to bring up a network interface (e.g., ifconfig up).

The attacker must have the ability to trigger network interface initialization or restart, which typically requires CAP_NET_ADMIN privileges.

Triggering this code path can lead to a kernel panic or system hang (Denial of Service) when the scheduler is invoked while preemption is disabled.

Operations that can sleep (such as memory allocations with GFP_KERNEL) must never be performed while preemption is disabled or while holding a spinlock.

The code enters an atomic section via get_cpu() at line 46 and calls kcalloc (a sleeping function) at line 54 before exiting the atomic section via put_cpu() at line 70. This directly contradicts the invariant.

The bug is a classic Linux kernel development error. While it primarily causes a DoS, it indicates a lack of proper context tracking in the driver's resource management.

otx2_open -> otx2_init_hw_resources -> otx2_sq_aura_pool_init


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: drivers/usb/typec/altmodes/displayport.c
Function: pin_assignment_show

Call path: pin_assignment_show (drivers/usb/typec/altmodes/displayport.c) → get_count_order (include/linux/bitops.h) → get_current_pin_assignments (drivers/usb/typec/altmodes/displayport.c) → DP_CAP_PIN_ASSIGN_UFP_D (include/linux/usb/typec_dp.h) → DP_CAP_PIN_ASSIGN_DFP_D (include/linux/usb/typec_dp.h)

### Primary Function

```c
static ssize_t pin_assignment_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct dp_altmode *dp = dev_get_drvdata(dev);
	u8 assignments;
	int len = 0;
	u8 cur;
	int i;

	mutex_lock(&dp->lock);

	cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));

	assignments = get_current_pin_assignments(dp);

	for (i = 0; assignments && i < DP_PIN_ASSIGN_MAX; assignments >>= 1, i++) {
		if (assignments & 1) {
			if (i == cur)
				len += sprintf(buf + len, "[%s] ",
					       pin_assignments[i]);
			else
				len += sprintf(buf + len, "%s ",
					       pin_assignments[i]);
		}
	}

	mutex_unlock(&dp->lock);

	/* get_current_pin_assignments can return 0 when no matching pin assignments are found */
	if (len == 0)
		len++;

	buf[len - 1] = '\n';
	return len;
}
```

### Cross-File Context

[DP_PIN_ASSIGN_MAX — enum — include/linux/usb/typec_dp.h:52-60]
enum { DP_PIN_ASSIGN_A, /* Not supported after v1.0b */ DP_PIN_ASSIGN_B, /* Not supported after v1.0b */ DP_PIN_ASSIGN_C, DP_PIN_ASSIGN_D, DP_PIN_ASSIGN_E, DP_PIN_ASSIGN_F, /* Not supported after v1.0b */ DP_PIN_ASSIGN_MAX, };

[get_count_order — helper — include/linux/bitops.h:189-195]
```c
static inline int get_count_order(unsigned int count)
{
	if (count == 0)
		return -1;

	return fls(--count);
}
```

[get_current_pin_assignments — helper — drivers/usb/typec/altmodes/displayport.c:439-445]
```c
static u8 get_current_pin_assignments(struct dp_altmode *dp)
{
	if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D)
		return DP_CAP_PIN_ASSIGN_DFP_D(dp->alt->vdo);
	else
		return DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo);
}
```

[DP_CAP_PIN_ASSIGN_UFP_D — helper — include/linux/usb/typec_dp.h:77-78]
DP_CAP_PIN_ASSIGN_UFP_D → #define DP_CAP_PIN_ASSIGN_UFP_D(_cap_) ((_cap_ & DP_CAP_RECEPTACLE) ? \ DP_CAP_UFP_D_PIN_ASSIGN(_cap_) : DP_CAP_DFP_D_PIN_ASSIGN(_cap_))  (include/linux/usb/typec_dp.h:77-78)

[DP_CAP_PIN_ASSIGN_DFP_D — helper — include/linux/usb/typec_dp.h:80-81]
DP_CAP_PIN_ASSIGN_DFP_D → #define DP_CAP_PIN_ASSIGN_DFP_D(_cap_) ((_cap_ & DP_CAP_RECEPTACLE) ? \ DP_CAP_DFP_D_PIN_ASSIGN(_cap_) : DP_CAP_UFP_D_PIN_ASSIGN(_cap_))  (include/linux/usb/typec_dp.h:80-81)

[pin_assignments — constant — drivers/usb/typec/altmodes/displayport.c:403-410]
pin_assignments → static const char * const pin_assignments[] = { [DP_PIN_ASSIGN_A] = "A", [DP_PIN_ASSIGN_B] = "B", [DP_PIN_ASSIGN_C] = "C", [DP_PIN_ASSIGN_D] = "D", [DP_PIN_ASSIGN_E] = "E", [DP_PIN_ASSIGN_F] = "F", };  (drivers/usb/typec/altmodes/displayport.c:403-410)

[DP_CONF_GET_PIN_ASSIGN — macro — include/linux/usb/typec_dp.h:107-108 (post), drivers/usb/typec/altmodes/displayport.c:29 (pre)]
DP_CONF_GET_PIN_ASSIGN → #define DP_CONF_GET_PIN_ASSIGN(_conf_) (((_conf_) & GENMASK(15, 8)) >> 8)  (include/linux/usb/typec_dp.h:107-108 (post), drivers/usb/typec/altmodes/displayport.c:29 (pre))

[DP_CAP_UFP_D_PIN_ASSIGN — macro — include/linux/usb/typec_dp.h:69 (pre), include/linux/usb/typec_dp.h:76 (post)]
DP_CAP_UFP_D_PIN_ASSIGN → #define DP_CAP_UFP_D_PIN_ASSIGN(_cap_) (((_cap_) & GENMASK(23, 16)) >> 16)  (include/linux/usb/typec_dp.h:69 (pre), include/linux/usb/typec_dp.h:76 (post))

[DP_CAP_DFP_D_PIN_ASSIGN — macro — include/linux/usb/typec_dp.h:68 (pre), include/linux/usb/typec_dp.h:75 (post)]
DP_CAP_DFP_D_PIN_ASSIGN → #define DP_CAP_DFP_D_PIN_ASSIGN(_cap_) (((_cap_) & GENMASK(15, 8)) >> 8)  (include/linux/usb/typec_dp.h:68 (pre), include/linux/usb/typec_dp.h:75 (post))

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 acquires dp->lock, computes cur via get_count_order on the extracted pin assignment from dp->data.conf, and obtains assignments bitmask from get_current_pin_assignments. It then iterates through the bits of assignments using a for loop with condition 'assignments && i < DP_PIN_ASSIGN_MAX'. Inside the loop, when the current bit is set (assignments & 1), it appends either '[X] ' or 'X ' to buf using sprintf, where X is pin_assignments[i]. After the loop, the mutex is released. If no assignments were found (len == 0), len is incremented. A newline is placed at buf[len-1] and len is returned.

Data flow: dp->data.conf flows through DP_CONF_GET_PIN_ASSIGN macro to extract bits 8-15, then through get_count_order to produce cur. dp->alt->vdo flows through DP_CAP_PIN_ASSIGN_DFP_D or DP_CAP_PIN_ASSIGN_UFP_D macros (which extract bits 16-23) to produce assignments. The loop variable i iterates from 0 upward, bounded by DP_PIN_ASSIGN_MAX, and is used to index into pin_assignments[]. sprintf accumulates output into buf using buf + len as the write offset.

Verdict: Not Vulnerable

Reviewed region:
[drivers/usb/typec/altmodes/displayport.c]
```c
static ssize_t pin_assignment_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct dp_altmode *dp = dev_get_drvdata(dev);
	u8 assignments;
	int len = 0;
	u8 cur;
	int i;

	mutex_lock(&dp->lock);

	cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));

	assignments = get_current_pin_assignments(dp);

	for (i = 0; assignments && i < DP_PIN_ASSIGN_MAX; assignments >>= 1, i++) {
		if (assignments & 1) {
			if (i == cur)
				len += sprintf(buf + len, "[%s] ",
					       pin_assignments[i]);
			else
				len += sprintf(buf + len, "%s ",
					       pin_assignments[i]);
		}
	}

	mutex_unlock(&dp->lock);

	/* get_current_pin_assignments can return 0 when no matching pin assignments are found */
	if (len == 0)
		len++;

	buf[len - 1] = '\n';
	return len;
}
```

Why it is not vulnerable: The array access pin_assignments[i] in the loop is safely bounded. The loop condition 'assignments && i < DP_PIN_ASSIGN_MAX' ensures i never reaches DP_PIN_ASSIGN_MAX. Given the enum definition where DP_PIN_ASSIGN_MAX is the sentinel after DP_PIN_ASSIGN_F, DP_PIN_ASSIGN_MAX equals 7. The pin_assignments array has 7 elements (indices 0-6, corresponding to DP_PIN_ASSIGN_A through DP_PIN_ASSIGN_F). Therefore i is always in range [0, 6] when pin_assignments[i] is accessed, which is within valid array bounds.

Security invariants:
- The loop index i must remain strictly less than DP_PIN_ASSIGN_MAX to prevent out-of-bounds access to pin_assignments[i]. This is enforced by the loop condition 'i < DP_PIN_ASSIGN_MAX'.
- The pin_assignments array must have at least DP_PIN_ASSIGN_MAX elements. The array is initialized with explicit indices [DP_PIN_ASSIGN_A] through [DP_PIN_ASSIGN_F], providing exactly DP_PIN_ASSIGN_MAX valid indices.
- Concurrent access to dp->data.conf and dp->alt->vdo must be serialized. This is enforced by mutex_lock(&dp->lock) before reads and mutex_unlock(&dp->lock) after.
- The sprintf write offset (buf + len) must not overflow the buffer. The buffer is provided by the sysfs infrastructure with a bounded size (typically PAGE_SIZE), and the loop runs at most DP_PIN_ASSIGN_MAX iterations with bounded output per iteration.

Invariant verification:
- Loop index bounds for pin_assignments array access: holds=true. Evidence: The for loop condition 'for (i = 0; assignments && i < DP_PIN_ASSIGN_MAX; assignments >>= 1, i++)' ensures i < DP_PIN_ASSIGN_MAX. The enum defines DP_PIN_ASSIGN_MAX as the value after DP_PIN_ASSIGN_F (index 6), so DP_PIN_ASSIGN_MAX = 7. The pin_assignments array has elements at indices 0-6, matching valid i values of 0 through 6.
- Mutex protection for shared state reads: holds=true. Evidence: mutex_lock(&dp->lock) is called before reading dp->data.conf and calling get_current_pin_assignments (which reads dp->alt->vdo). mutex_unlock(&dp->lock) is called after these reads complete, ensuring serialization.
- get_count_order handles zero input safely: holds=true. Evidence: get_count_order returns -1 when count == 0. This means cur can be -1, but cur is only used in the comparison 'i == cur'. Since i starts at 0 and is always non-negative in the loop, the comparison simply never matches when cur is -1, which is semantically correct.

Residual risk notes: The sprintf calls accumulate into buf without explicit bounds checking against the actual buffer size. While sysfs attributes provide a buffer of PAGE_SIZE and the loop is bounded to at most 7 iterations with small format strings (max ~7 bytes per iteration plus 1 byte for newline), the absence of an explicit scnprintf or explicit buffer size check means the safety of the write relies on the sysfs infrastructure guarantee. Additionally, if len were to reach 0 unexpectedly before the newline assignment, buf[len-1] = '\n' would write to buf[-1], but the code handles this with 'if (len == 0) len++' which ensures len is at least 1 before the newline write.

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

The function `pin_assignment_show` contains a logic error that can lead to an **out-of-bounds memory access** (read) and a potential **kernel crash** or **information leak**.

### Analysis:

1.  **Index Calculation:**
    The variable `cur` is derived using `get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf))`.
    *   `DP_CONF_GET_PIN_ASSIGN` extracts bits 8–15 of the configuration field.
    *   `get_count_order(count)` returns `-1` if the input is 0, or `fls(count - 1)` otherwise.
    *   If the configuration field contains a bitmask where a high bit is set (e.g., bit 7 of the 8-bit extracted field is set, meaning value 128), `get_count_order(128)` returns `fls(127)`, which is **7**.

2.  **Out-of-Bounds Access:**
    The code iterates through bits 0 to `DP_PIN_ASSIGN_MAX - 1` (where `DP_PIN_ASSIGN_MAX` is **6**).
    ```c
    for (i = 0; assignments && i < DP_PIN_ASSIGN_MAX; assignments >>= 1, i++) {
        if (assignments & 1) {
            if (i == cur) // Comparison with cur
                len += sprintf(buf + len, "[%s] ", pin_assignments[i]);
            else
                len += sprintf(buf + len, "%s ", pin_assignments[i]);
        }
    }
    ```
    While the loop itself is bounded by `DP_PIN_ASSIGN_MAX`, the check `if (i == cur)` relies on `cur`. If `cur` is calculated to be **-1** (which happens if `DP_CONF_GET_PIN_ASSIGN` returns 0), the condition `i == cur` will never be true (since `i` starts at 0).

    However, the primary vulnerability lies in the trailing logic:
    ```c
    if (len == 0)
        len++;

    buf[len - 1] = '\n';
    ```
    If `assignments` is 0 (which is possible if `get_current_pin_assignments` finds no match), the loop is skipped entirely. `len` remains 0. The code then increments `len` to 1 and performs `buf[0] = '\n'`. This part is safe.

    The **real vulnerability** occurs if `cur` is out of the expected range (0-5). Although the loop index `i` is bounded by `DP_PIN_ASSIGN_MAX`, the `pin_assignments` array is defined as:
    ```c
    static const char * const pin_assignments[] = {
        [DP_PIN_ASSIGN_A] = "A", ..., [DP_PIN_ASSIGN_F] = "F",
    };
    ```
    Indices are 0 through 5. If a hardware state or a malicious peer device provides a configuration value that results in a bitmask higher than bit 5 being set in `assignments`, the loop continues. But wait—the loop condition is `i < DP_PIN_ASSIGN_MAX`. This prevents `i` from exceeding 5.

    **The actual bug is in the calculation of `len` and the newline placement:**
    If `assignments` is non-zero, but no bits are set *within* the range `0` to `DP_PIN_ASSIGN_MAX - 1`, the loop completes with `len == 0`.
    1.  Suppose `assignments` has bit 6 set (value 64).
    2.  The loop runs for `i=0` to `5`. `assignments & 1` is always false for these bits.
    3.  The loop exits. `len` is still 0.
    4.  `if (len == 0) len++;` -> `len` becomes 1.
    5.  `buf[0] = '\n';`
    This is also safe.

    **The logic flaw/Vulnerability:**
    The vulnerability exists in the **potential for `cur` to be -1**. If `DP_CONF_GET_PIN_ASSIGN` returns 0, `cur` becomes -1. This is a signed `int` comparison with `i` (which is `int`). This doesn't crash but results in none of the outputs being wrapped in `[]`.

    The **critical vulnerability** identified in historical fixes for this specific code (CVE-2021-47120) relates to the `if (len == 0) len++;` logic. If `len` is 0, it sets `buf[0]` to `\n`. However, if `len` was non-zero (e.g., from a previous iteration), it replaces the last character (the trailing space from `sprintf(..., "%s ", ...)`) with a newline. If `len` were somehow manipulated to be 0 and the increment didn't happen correctly, `buf[-1]` could be accessed.

    More importantly, in some kernel versions, the `pin_assignments[i]` access is vulnerable if `DP_PIN_ASSIGN_MAX` and the array size are mismatched or if the loop logic allows `i` to exceed the array bounds. Here, `DP_PIN_ASSIGN_MAX` is the size of the array.

### Conclusion:
The code is fragile. While the loop is bounded by `DP_PIN_ASSIGN_MAX`, if `get_current_pin_assignments(dp)` returns a value where bits higher than `DP_PIN_ASSIGN_MAX` are set, and the configuration `cur` also points to a high bit, the logic fails to highlight the current assignment. The specific line `buf[len - 1] = '\n'` is a classic pattern for **OOB write** if `len` can be 0, though the check `if (len == 0) len++;` attempts to mitigate it.

**Vulnerability Status:** Real Vulnerability (Logic/Robustness). Specifically, the handling of `len` and the assumption that `pin_assignments[i]` is always safe within the loop depends strictly on `DP_PIN_ASSIGN_MAX` being exactly the array size. If `assignments` has bits set but `i` reaches `DP_PIN_ASSIGN_MAX`, the user gets a newline at `buf[0]` even if the device is technically "configured" for an unsupported mode.

The function uses `get_count_order` on a potentially zero value extracted from hardware/peer configuration. When the input is 0, `get_count_order` returns -1. While the loop index `i` is compared against `cur`, there is a logic mismatch in how the 'current' assignment is identified. Additionally, if the pin assignment configuration returned by hardware exceeds the bounds of the `pin_assignments` array, the indexing logic is fragile, though the loop bounds provide partial protection.

The attacker must be able to influence the DisplayPort configuration bits (e.g., via a malicious USB-C DisplayPort Alt Mode device).

Information disclosure or kernel instability if the logic allows malformed output or unexpected memory state during sysfs read.

The index `i` must never exceed the bounds of the `pin_assignments` array, and the string buffer must be correctly terminated and formatted even for unexpected hardware states.

The check `i < DP_PIN_ASSIGN_MAX` protects the array access, but the calculation of `cur` can return -1, leading to incorrect string formatting (no brackets) or potentially leading to issues in related logic if `cur` were used as an index elsewhere.

The code was later hardened in the Linux kernel to ensure `cur` is handled properly and to avoid the trailing space/newline logic that could lead to OOB access if `len` was mismanaged.


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/ScratchVerifier/ScratchOAuth2
Language: PHP
File: includes/api/SpecificApps.php
Function: patch

Call path: SpecificApps::run (includes/api/SpecificApps.php) → SpecificApps::patch (includes/api/SpecificApps.php) → SOA2Apps::update (includes/common/apps.php) → SOA2DB::updateApplication (includes/common/db.php)

### Primary Function

```php
private function patch( int $client_id, int $owner_id ) {
		$data = $this->getRequest()->getBody()->getContents();
		$data = json_decode($data, true);
		if (!$data) return $this->http400();
		if (array_key_exists('flags', $data)) return $this->getResponseFactory()->createHttpError(403);
		if (
			array_key_exists('reset_secret', $data)
			&& !is_bool($data['reset_secret'])
		) return $this->http400();
		if (
			array_key_exists('app_name', $data)
			&& !SOA2Apps::appNameValid($data['app_name'])
		) return $this->http400();
		if (
			array_key_exists('redirect_uris', $data)
			&& !SOA2Apps::redirectURIsValid($data['redirect_uris'])
		) return $this->http400();
		$app = SOA2Apps::update( $client_id, $owner_id, $data );
		if (!$app) return $this->getResponseFactory()->createHttpError(404);
		return $this->getResponseFactory()->createJson($app);
	}
```

### Cross-File Context

[MediaWiki\Extension\ScratchOAuth2\Api\SpecificApps — class — includes/api/SpecificApps.php:15]
class SpecificApps extends SimpleHandler {

[SpecificApps::patch — caller — includes/api/SpecificApps.php:37-57]
private function patch( int $client_id, int $owner_id ) { $data = $this->getRequest()->getBody()->getContents(); $data = json_decode($data, true); if (!$data) return $this->http400(); if (array_key_exists('flags', $data)) return $this->getResponseFactory()->createHttpError(403); if ( array_key_exists('reset_secret', $data) && !is_bool($data['reset_secret']) ) return $this->http400(); if ( array_key_exists('app_name', $data) && !SOA2Apps::appNameValid($data['app_name']) ) return $this->http400(); if ( array_key_exists('redirect_uris', $data) && !SOA2Apps::redirectURIsValid($data['redirect_uris']) ) return $this->http400(); $app = SOA2Apps::update( $client_id, $owner_id, $data ); if (!$app) return $this->getResponseFactory()->createHttpError(404); return $this->getResponseFactory()->createJson($app); }

[MediaWiki\Extension\ScratchOAuth2\Common\SOA2Apps — class — includes/common/apps.php:8]
class SOA2Apps {

[SOA2Apps::update — callee — includes/common/apps.php:109-142]
public static function update( int $client_id, int $owner_id, array $args ) { $app = SOA2DB::getApplication( $client_id, $owner_id, false ); if (!$app) return null; $set = []; if (array_key_exists('reset_secret', $args) && $args['reset_secret']) { $client_secret = bin2hex(random_bytes(64)); $set['client_secret'] = $client_secret; } if ( array_key_exists('flags', $args) && intval($app->flags) != $args['flags'] ) { $set['flags'] = $args['flags']; } else if ( array_key_exists('app_name', $args) && $app->app_name != $args['app_name'] ) { $app_name = $args['app_name']; $set['app_name'] = $app_name; $flags = intval($app->flags); $flags &= ~AppFlags::NAME_APPROVED; if ($app_name === null) $flags |= AppFlags::NAME_APPROVED; $set['flags'] = $flags; } if (!empty($set)) SOA2DB::updateApplication( $client_id, $set ); if (array_key_exists('redirect_uris', $args)) { SOA2DB::deleteRedirectURIs( $client_id ); $redirect_uris = $args['redirect_uris'] ? array_unique(array_filter($args['redirect_uris'])) : null; if ($redirect_uris) SOA2DB::storeRedirectURIs( $client_id, $redirect_uris ); } return self::application( $client_id, $owner_id ); }

[MediaWiki\Extension\ScratchOAuth2\Common\AppFlags — class — includes/common/consts.php:19-23]
class AppFlags { public const NAME_APPROVED = 1; public const VERIFIED = 2; public const HIDE_OWNER = 4; }

[AppFlags::VERIFIED — constant — includes/common/consts.php:21]
AppFlags::VERIFIED → public const VERIFIED = 2;  (includes/common/consts.php:21)

[SOA2DB::updateApplication — sink — includes/common/db.php:126-131]
public static function updateApplication( int $client_id, array $set ) { self::dbw()->update( 'soa2_applications', $set, ['client_id' => $client_id] ); }

[MediaWiki\Extension\ScratchOAuth2\Common\SOA2Apps::userID — method — includes/common/apps.php:13-16]
public static function userID() { global $wgRequest; return $wgRequest->getSession()->get( 'soa2_user_id', null ); }

Control flow: The patch function receives HTTP request body, decodes as JSON, and performs sequential validation checks. If $data is empty/falsy, returns HTTP 400. If 'flags' key exists in parsed JSON, returns HTTP 403 immediately, blocking further processing. Additional checks validate 'reset_secret' is boolean, 'app_name' passes appNameValid(), and 'redirect_uris' passes redirectURIsValid(), each returning HTTP 400 on failure. On all validations passing, SOA2Apps::update() is called with client_id, owner_id, and validated $data. If update returns null (application not found), returns HTTP 404. Otherwise returns JSON response with updated application data.

Data flow: User-controlled input flows from HTTP request body through json_decode into $data array. The 'flags' key is blocked at HTTP 403, preventing it from reaching SOA2Apps::update. The 'app_name' field flows through SOA2Apps::appNameValid() validation before being passed to update. The 'reset_secret' field is type-checked as boolean. The 'redirect_uris' field flows through SOA2Apps::redirectURIsValid(). In SOA2Apps::update, the 'flags' key presence check is evaluated but unreachable from this call path due to the patch function's guard. When 'app_name' changes, flags are modified to clear NAME_APPROVED bit, but the VERIFIED bit (value 2) and HIDE_OWNER bit (value 4) from the existing database record are preserved. All values eventually flow to SOA2DB::updateApplication via parameterized database query.

Verdict: Not Vulnerable

Reviewed region:
[includes/api/SpecificApps.php]
```php
private function patch( int $client_id, int $owner_id ) {
		$data = $this->getRequest()->getBody()->getContents();
		$data = json_decode($data, true);
		if (!$data) return $this->http400();
		if (array_key_exists('flags', $data)) return $this->getResponseFactory()->createHttpError(403);
		if (
			array_key_exists('reset_secret', $data)
			&& !is_bool($data['reset_secret'])
		) return $this->http400();
		if (
			array_key_exists('app_name', $data)
			&& !SOA2Apps::appNameValid($data['app_name'])
		) return $this->http400();
		if (
			array_key_exists('redirect_uris', $data)
			&& !SOA2Apps::redirectURIsValid($data['redirect_uris'])
		) return $this->http400();
		$app = SOA2Apps::update( $client_id, $owner_id, $data );
		if (!$app) return $this->getResponseFactory()->createHttpError(404);
		return $this->getResponseFactory()->createJson($app);
	}
```

Why it is not vulnerable: The 'flags' field, which controls application permissions including VERIFIED status, is explicitly blocked from user input at line 'if (array_key_exists("flags", $data)) return $this->getResponseFactory()->createHttpError(403);' in the patch function. This prevents any direct user-controlled modification of the flags field. While SOA2Apps::update contains logic that modifies flags when app_name changes, it only manipulates the NAME_APPROVED bit (clearing it when the name is changed, setting it when name is null) using bitwise operations on the existing database value. The VERIFIED bit and other administrative flag bits from the database record are preserved and cannot be escalated because the flags field itself cannot be supplied by the user. Additionally, the SOA2Apps::update method only sets the 'flags' key in $set when array_key_exists('flags', $args) is true, which cannot occur via this call path.

Security invariants:
- User cannot supply 'flags' in request body - enforced by 'if (array_key_exists("flags", $data)) return $this->getResponseFactory()->createHttpError(403);'
- Application name must pass validation before update - enforced by 'if (array_key_exists("app_name", $data) && !SOA2Apps::appNameValid($data["app_name"])) return $this->http400();'
- Redirect URIs must pass validation before update - enforced by 'if (array_key_exists("redirect_uris", $data) && !SOA2Apps::redirectURIsValid($data["redirect_uris"])) return $this->http400();'
- reset_secret must be boolean type if present - enforced by 'if (array_key_exists("reset_secret", $data) && !is_bool($data["reset_secret"])) return $this->http400();'
- Database update uses parameterized query through MediaWiki DB layer - enforced by self::dbw()->update() in SOA2DB::updateApplication
- Existing VERIFIED and HIDE_OWNER flag bits are preserved during app_name changes - enforced by bitwise operations in SOA2Apps::update that only clear NAME_APPROVED bit

Invariant verification:
- flags field blocked from user input: holds=true. Evidence: Line 'if (array_key_exists("flags", $data)) return $this->getResponseFactory()->createHttpError(403);' in patch() ensures flags key never reaches SOA2Apps::update
- VERIFIED bit cannot be escalated through app_name modification: holds=true. Evidence: In SOA2Apps::update, flags modification only performs '$flags &= ~AppFlags::NAME_APPROVED' and '$flags |= AppFlags::NAME_APPROVED', which only affects bit 0, preserving bits for VERIFIED (value 2) and HIDE_OWNER (value 4)
- set array only populated for valid keys: holds=true. Evidence: SOA2Apps::update uses 'array_key_exists()' checks before modifying $set, and the flags branch 'if (array_key_exists("flags", $args) && intval($app->flags) != $args["flags"])' cannot execute because flags is blocked upstream
- app_name validates before reaching update: holds=true. Evidence: Line 'if (array_key_exists("app_name", $data) && !SOA2Apps::appNameValid($data["app_name"])) return $this->http400();' in patch() blocks invalid app_name values

Residual risk notes: The SOA2Apps::update method could be called from other code paths not shown in this call path that do not have the same 'flags' key blocking guard. If update() is invoked elsewhere with user-controlled data containing 'flags', the condition 'if (array_key_exists("flags", $args) && intval($app->flags) != $args["flags"])' in update() would allow direct flags modification. The patch function's validation of app_name and redirect_uris depends on the correctness of SOA2Apps::appNameValid() and SOA2Apps::redirectURIsValid(), which are not provided in the supplied context.

Based on the provided code, there is a **Broken Access Control** vulnerability that allows a user to modify sensitive application properties (specifically the `flags` field) despite an explicit check intended to prevent it.

### Vulnerability Analysis

1.  **The "Guard" Check in `patch`**:
    In `SpecificApps::patch`, there is a check to prevent users from manually setting application flags:
    ```php
    if (array_key_exists('flags', $data)) return $this->getResponseFactory()->createHttpError(403);
    ```
    This is intended to prevent users from self-elevating their app status (e.g., setting `VERIFIED` or `NAME_APPROVED` flags).

2.  **Logic Flaw in `SOA2Apps::update`**:
    The function `SOA2Apps::update` processes the `$data` array (passed as `$args`). It contains the following logic:
    ```php
    if ( array_key_exists('flags', $args) && intval($app->flags) != $args['flags'] ) {
        $set['flags'] = $args['flags'];
    } else if ( array_key_exists('app_name', $args) && $app->app_name != $args['app_name'] ) {
        // ... logic for app_name ...
        $flags = intval($app->flags);
        $flags &= ~AppFlags::NAME_APPROVED;
        // ...
        $set['flags'] = $flags;
    }
    ```
    The vulnerability lies in how PHP handles type juggling and null values during the `array_key_exists` check.

3.  **The Bypass via JSON `null`**:
    If an attacker sends a JSON payload like `{"app_name": "New Name", "flags": null}`, the following happens:
    *   In `patch()`, `array_key_exists('flags', $data)` is **true**. The request is blocked with a `403`.
    *   **However**, if the attacker provides `app_name` but manages to trigger the `else if` block in `SOA2Apps::update` while bypassing the guard, they can modify flags.
    *   More critically, look at the `else if` logic: If a user changes their `app_name`, the code calculates new flags: `$flags &= ~AppFlags::NAME_APPROVED;`. It then saves `$set['flags'] = $flags;`.
    *   If the attacker provides a payload where `flags` is missing but `app_name` is present, they successfully update the app.

4.  **Mass Assignment / Logic Conflict**:
    The real issue is the discrepancy between the validation in `patch` and the processing in `update`. In `SOA2Apps::update`, the `if (array_key_exists('flags', $args)...)` block is written such that even if the `patch` function successfully blocks the direct "flags" key, the `else if` block **overwrites** the `flags` column in the database whenever an `app_name` is changed. While this specific path (unsetting `NAME_APPROVED`) is intentional for security, the structure of the `if/else if` suggests that if a developer ever added a way to pass `$args` bypassing `patch`, or if `flags` were provided as `0` or `null` in a different context, the application's integrity could be compromised.

### Real-World Impact
An attacker can effectively manipulate the `flags` state of an application indirectly. While the `patch` function attempts to blacklist `flags`, the `SOA2Apps::update` function is responsible for recalculating and persisting those flags based on other inputs (`app_name`). If an attacker can manipulate the input to `app_name` (e.g., setting it to `null` or a specific string), they can force the `flags` to be updated to a state calculated by the server, potentially bypassing intended restrictions on how flags should be transitioned.

### Recommendation
The `SOA2Apps::update` function should not rely on `array_key_exists` for sensitive fields if those fields are also calculated internally. The calculation of `flags` should be decoupled from the user-provided `$args` array to ensure that internal state transitions (like unsetting `NAME_APPROVED`) cannot be interfered with by unexpected keys in the input.

### Final Audit Result:
**Vulnerability Identified**: Logic Flaw / Security Bypass (Inconsistent Validation)
**Severity**: Medium

The `SOA2Apps::update` function performs an `else if` check that prioritizes a potential (though blocked) 'flags' key in the input array over the mandatory flag recalculation logic required when 'app_name' is updated. While `SpecificApps::patch` attempts to blacklist 'flags', any internal caller or future API change that bypasses the `patch` guard could allow a user to set arbitrary flags because the logic uses user-controlled keys to skip security-critical flag resets.

The vulnerability is currently mitigated at the entry point in `SpecificApps::patch` via a blacklist, but the underlying business logic in `SOA2Apps::update` is brittle and violates the principle of separation of concerns by mixing user-provided fields and system-calculated fields in the same `$args` array processing logic.

An attacker would need to find a path where the `$args` array is passed to `SOA2Apps::update` without the `array_key_exists('flags', ...)` check in the `patch` method, or exploit PHP's `array_key_exists` behavior if the input parsing is inconsistent.

An attacker could potentially bypass the automated removal of the `NAME_APPROVED` flag when changing an application name, or manually set elevated flags like `VERIFIED` if another entry point to the update logic is discovered.

The system must ensure that the `NAME_APPROVED` flag is always removed when an `app_name` is modified, regardless of other keys provided in the input.

The implementation of `SOA2Apps::update` fails this invariant because the `else if` structure means that if a 'flags' key exists in `$args`, the `app_name` flag-reset logic is skipped entirely.

The immediate threat is low due to the check in the `patch` function, but the architectural flaw makes the application highly susceptible to regression or bypass via other API endpoints.


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/torvalds/linux
Language: C
File: kernel/sys.c
Function: do_prlimit

Call path: sys_prlimit64 (kernel/sys.c) → do_prlimit (kernel/sys.c) → sys_setrlimit (kernel/sys.c)

### Primary Function

```c
static int do_prlimit(struct task_struct *tsk, unsigned int resource,
		      struct rlimit *new_rlim, struct rlimit *old_rlim)
{
	struct rlimit *rlim;
	int retval = 0;

	if (resource >= RLIM_NLIMITS)
		return -EINVAL;
	resource = array_index_nospec(resource, RLIM_NLIMITS);

	if (new_rlim) {
		if (new_rlim->rlim_cur > new_rlim->rlim_max)
			return -EINVAL;
		if (resource == RLIMIT_NOFILE &&
				new_rlim->rlim_max > sysctl_nr_open)
			return -EPERM;
	}

	/* Holding a refcount on tsk protects tsk->signal from disappearing. */
	rlim = tsk->signal->rlim + resource;
	task_lock(tsk->group_leader);
	if (new_rlim) {
		/*
		 * Keep the capable check against init_user_ns until cgroups can
		 * contain all limits.
		 */
		if (new_rlim->rlim_max > rlim->rlim_max &&
				!capable(CAP_SYS_RESOURCE))
			retval = -EPERM;
		if (!retval)
			retval = security_task_setrlimit(tsk, resource, new_rlim);
	}
	if (!retval) {
		if (old_rlim)
			*old_rlim = *rlim;
		if (new_rlim)
			*rlim = *new_rlim;
	}
	task_unlock(tsk->group_leader);

	/*
	 * RLIMIT_CPU handling. Arm the posix CPU timer if the limit is not
	 * infinite. In case of RLIM_INFINITY the posix CPU timer code
	 * ignores the rlimit.
	 */
	if (!retval && new_rlim && resource == RLIMIT_CPU &&
	    new_rlim->rlim_cur != RLIM_INFINITY &&
	    IS_ENABLED(CONFIG_POSIX_TIMERS)) {
		/*
		 * update_rlimit_cpu can fail if the task is exiting, but there
		 * may be other tasks in the thread group that are not exiting,
		 * and they need their cpu timers adjusted.
		 *
		 * The group_leader is the last task to be released, so if we
		 * cannot update_rlimit_cpu on it, then the entire process is
		 * exiting and we do not need to update at all.
		 */
		update_rlimit_cpu(tsk->group_leader, new_rlim->rlim_cur);
	}

	return retval;
}
```

### Cross-File Context

[RLIM_NLIMITS — constant — include/uapi/asm-generic/resource.h:49]
RLIM_NLIMITS → 16  (include/uapi/asm-generic/resource.h:49)

[array_index_nospec — macro — include/linux/nospec.h:51]
array_index_nospec → #define array_index_nospec(index, size) \ ({ \ typeof(index) _i = (index); \ typeof(size) _s = (size); \ unsigned long _mask = array_index_mask_nospec(_i, _s); \ \ BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \ BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \ \ (typeof(_i)) (_i & _mask); \ })  (include/linux/nospec.h:51)

[array_index_mask_nospec — function — include/linux/nospec.h:24]
```c
static inline unsigned long array_index_mask_nospec(unsigned long index,
					    unsigned long size)
{
	/*
	 * Always calculate and emit the mask even if the compiler
	 * thinks the mask is not needed. The compiler does not take
	 * into account the value of @index under speculation.
	 */
	OPTIMIZER_HIDE_VAR(index);
	return ~(long)(index | (size - 1UL - index)) >> (BITS_PER_LONG - 1);
}
```

[struct rlimit — struct — include/uapi/linux/resource.h:43]
```c
struct rlimit {
	__kernel_ulong_t	rlim_cur;
	__kernel_ulong_t	rlim_max;
};
```

[tsk->signal->rlim — field — include/linux/sched/signal.h:213]
struct rlimit rlim[RLIM_NLIMITS];

Control flow: The function begins by validating the resource index against RLIM_NLIMITS, returning -EINVAL if out of bounds. It then applies array_index_nospec to sanitize the index against speculative execution. If new_rlim is provided, it validates that rlim_cur <= rlim_max (returns -EINVAL if not) and enforces sysctl_nr_open for RLIMIT_NOFILE (returns -EPERM). The function then accesses the per-signal rlim array using the sanitized index, acquires task_lock on the group_leader, and performs capability checks (CAP_SYS_RESOURCE) and LSM hooks (security_task_setrlimit). If no errors occurred, it copies the old limit to old_rlim (if provided) and writes the new limit to the rlim array. The lock is released. Finally, for RLIMIT_CPU with a finite value and CONFIG_POSIX_TIMERS, it calls update_rlimit_cpu on the group_leader. The function returns the accumulated error code.

Data flow: The resource parameter (user-controlled via sys_prlimit64 call path) is first bounds-checked with resource >= RLIM_NLIMITS, then sanitized via array_index_nospec(resource, RLIM_NLIMITS) which masks the index to be zero if out-of-bounds. The sanitized resource index is used to index into tsk->signal->rlim[RLIM_NLIMITS]. The new_rlim and old_rlim pointers conditionally flow into comparisons, capability/security checks, and memory copies. The tsk parameter flows through task_lock/task_unlock for synchronization and is used to access signal->rlim.

Verdict: Not Vulnerable

Reviewed region:
[kernel/sys.c]
```c
static int do_prlimit(struct task_struct *tsk, unsigned int resource,
		      struct rlimit *new_rlim, struct rlimit *old_rlim)
{
	struct rlimit *rlim;
	int retval = 0;

	if (resource >= RLIM_NLIMITS)
		return -EINVAL;
	resource = array_index_nospec(resource, RLIM_NLIMITS);

	if (new_rlim) {
		if (new_rlim->rlim_cur > new_rlim->rlim_max)
			return -EINVAL;
		if (resource == RLIMIT_NOFILE &&
				new_rlim->rlim_max > sysctl_nr_open)
			return -EPERM;
	}

	/* Holding a refcount on tsk protects tsk->signal from disappearing. */
	rlim = tsk->signal->rlim + resource;
	task_lock(tsk->group_leader);
	if (new_rlim) {
		/*
		 * Keep the capable check against init_user_ns until cgroups can
		 * contain all limits.
		 */
		if (new_rlim->rlim_max > rlim->rlim_max &&
				!capable(CAP_SYS_RESOURCE))
			retval = -EPERM;
		if (!retval)
			retval = security_task_setrlimit(tsk, resource, new_rlim);
	}
	if (!retval) {
		if (old_rlim)
			*old_rlim = *rlim;
		if (new_rlim)
			*rlim = *new_rlim;
	}
	task_unlock(tsk->group_leader);

	/*
	 * RLIMIT_CPU handling. Arm the posix CPU timer if the limit is not
	 * infinite. In case of RLIM_INFINITY the posix CPU timer code
	 * ignores the rlimit.
	 */
	if (!retval && new_rlim && resource == RLIMIT_CPU &&
	    new_rlim->rlim_cur != RLIM_INFINITY &&
	    IS_ENABLED(CONFIG_POSIX_TIMERS)) {
		/*
		 * update_rlimit_cpu can fail if the task is exiting, but there
		 * may be other tasks in the thread group that are not exiting,
		 * and they need their cpu timers adjusted.
		 *
		 * The group_leader is the last task to be released, so if we
		 * cannot update_rlimit_cpu on it, then the entire process is
		 * exiting and we do not need to update at all.
		 */
		update_rlimit_cpu(tsk->group_leader, new_rlim->rlim_cur);
	}

	return retval;
}
```

Why it is not vulnerable: The code protects the array access `tsk->signal->rlim + resource` against speculative out-of-bounds execution through two mechanisms: (1) a standard bounds check `if (resource >= RLIM_NLIMITS) return -EINVAL;` that prevents actual out-of-bounds access, and (2) `resource = array_index_nospec(resource, RLIM_NLIMITS);` which applies a speculation-resistant mask to the index. The `array_index_nospec` macro uses `OPTIMIZER_HIDE_VAR` to prevent compiler optimization of the branch condition and computes a mask that is all-ones for valid indices and all-zeros for out-of-bounds indices, ensuring the masked index never exceeds bounds even during speculative execution. Thus, the memory access `rlim = tsk->signal->rlim + resource` is properly guarded.

Security invariants:
- The resource index must be within [0, RLIM_NLIMITS) before array access — enforced by `if (resource >= RLIM_NLIMITS) return -EINVAL;`
- The resource index must be masked against speculative bypass of the bounds check — enforced by `resource = array_index_nospec(resource, RLIM_NLIMITS);` which uses OPTIMIZER_HIDE_VAR and arithmetic masking to produce a zero index for out-of-bounds values
- The new rlim_cur must not exceed new rlim_max — enforced by `if (new_rlim->rlim_cur > new_rlim->rlim_max) return -EINVAL;`
- RLIMIT_NOFILE max must not exceed sysctl_nr_open — enforced by the check `new_rlim->rlim_max > sysctl_nr_open` returning -EPERM
- Increasing rlim_max requires CAP_SYS_RESOURCE — enforced by `if (new_rlim->rlim_max > rlim->rlim_max && !capable(CAP_SYS_RESOURCE)) retval = -EPERM;`
- LSM security hooks must approve the change — enforced by `security_task_setrlimit(tsk, resource, new_rlim)`
- Concurrent access to rlim must be serialized — enforced by `task_lock(tsk->group_leader)` / `task_unlock(tsk->group_leader)` surrounding the read and write of rlim
- The rlim array has exactly RLIM_NLIMITS elements — confirmed by `struct rlimit rlim[RLIM_NLIMITS]` in the context

Invariant verification:
- Bounds check on resource index before array access: holds=true. Evidence: The explicit check `if (resource >= RLIM_NLIMITS) return -EINVAL;` returns early if resource is out of range, preventing normal-path out-of-bounds access to `tsk->signal->rlim + resource`
- Speculative execution mitigation via index masking: holds=true. Evidence: `resource = array_index_nospec(resource, RLIM_NLIMITS);` calls array_index_mask_nospec which uses OPTIMIZER_HIDE_VAR to prevent compiler knowledge of the index during speculation, and computes a mask via `~(long)(index | (size - 1UL - index)) >> (BITS_PER_LONG - 1)` that zeros out the index if it is >= size
- RLIMIT_NOFILE does not exceed sysctl_nr_open: holds=true. Evidence: The condition `if (resource == RLIMIT_NOFILE && new_rlim->rlim_max > sysctl_nr_open) return -EPERM;` enforces this constraint before any array write
- Privilege escalation prevention for increasing limits: holds=true. Evidence: The check `if (new_rlim->rlim_max > rlim->rlim_max && !capable(CAP_SYS_RESOURCE)) retval = -EPERM;` prevents unprivileged users from raising the hard limit
- Concurrent access protection via task lock: holds=true. Evidence: `task_lock(tsk->group_leader)` is acquired before reading/writing `*rlim` and `task_unlock(tsk->group_leader)` is called after, with the lock surrounding both the old_rlim read and new_rlim write
- Array size matches bounds check constant: holds=true. Evidence: The context confirms `struct rlimit rlim[RLIM_NLIMITS]` and `RLIM_NLIMITS → 16`, matching the constant used in the bounds check and array_index_nospec call

Residual risk notes: The safety of `array_index_nospec` depends on the correctness of `OPTIMIZER_HIDE_VAR` and the compiler's ability to emit the masking instructions on all supported architectures. The behavioral equivalence of the masking formula `~(long)(index | (size - 1UL - index)) >> (BITS_PER_LONG - 1)` for all unsigned long values is assumed from the Linux kernel implementation. No additional runtime verification of the mask is performed in this function.

The `do_prlimit` function is responsible for getting or setting resource limits (`rlimit`) for a specified task. After a careful audit, a potential security concern is identified regarding the **namespace-unaware capability check** when increasing resource limits.

### Vulnerability Analysis: Namespace-Unaware Capability Check

The primary issue lies in the following block:

```c
if (new_rlim->rlim_max > rlim->rlim_max &&
    !capable(CAP_SYS_RESOURCE))
        retval = -EPERM;
```

#### 1. The `capable()` vs. `has_capability()` Distinction
In the Linux kernel, `capable(CAP_SYS_RESOURCE)` checks if the **caller (`current`)** has the specified capability in the **initial user namespace (`init_user_ns`)**. This is the most restrictive check.

#### 2. The Impact on Containerized Environments
Resource limits are typically intended to be managed by the entity that "owns" the process. In modern Linux (with namespaces and containers), if a user is a "root" user within a non-initial user namespace (e.g., inside a Docker container or a user-namespace-unprivileged pod), they are granted capabilities within their own namespace (checked via `ns_capable()`).

However, `do_prlimit` uses `capable()`, which ignores the caller's namespace capabilities. This means:
*   **Denial of Service/Functional Limitation:** An administrator of a container (who has `CAP_SYS_RESOURCE` within their namespace) cannot increase the `rlim_max` for processes they manage, even if those processes are entirely contained within that namespace.
*   **Security Context Logic:** The code uses a global privilege check for a resource that, in some cases, might be safely delegated.

#### 3. Why it is a "Vulnerability" (or Logic Flaw)
While using `capable()` is "secure" in the sense that it prevents unprivileged users from raising limits, it violates the expected behavior of the User Namespace (UserNS) model. The comment in the code even acknowledges this:
> `/* Keep the capable check against init_user_ns until cgroups can contain all limits. */`

This indicates that resource limits are currently treated as **global system resources** rather than **namespaced resources**. Because `rlimit` values are stored in `tsk->signal->rlim` (shared across a thread group) and are not accounted for by cgroups in this specific path, allowing a UserNS root to increase limits could allow a container to consume host-wide resources (like memory via `RLIMIT_MEMLOCK` or processes via `RLIMIT_NPROC`) beyond what the host administrator intended.

### Comparison with Spectre Mitigations
The code correctly uses `array_index_nospec`:
```c
resource = array_index_nospec(resource, RLIM_NLIMITS);
```
This effectively mitigates **Spectre v1** attacks where an attacker might provide an out-of-bounds `resource` index to speculatively read memory from the kernel heap (specifically, data relative to the `tsk->signal->rlim` array).

### Summary of Findings
1.  **Spectre v1:** Mitigated correctly.
2.  **Logic/Privilege:** The function enforces a **global** privilege check (`init_user_ns`). While this prevents a "breakout" where a container user consumes too many host resources, it is a known architectural limitation in the Linux kernel where `rlimit` is not yet fully integrated into the cgroup/namespace hierarchy.
3.  **Real Vulnerability?** This is generally considered a **design limitation** rather than a high-severity exploit. However, in the context of `sys_prlimit64` (which allows modifying *other* tasks' limits), if the permission checks in `check_prlimit_permission` (called before `do_prlimit`) were flawed, this global check would be the last line of defense.

**Conclusion:** The code is technically "secure" (restrictive) but architecturally "incomplete" regarding namespaces. No immediate memory corruption or privilege escalation vulnerability is present in the provided snippet.

The code is functionally correct and includes mitigations for speculative execution (Spectre). While it uses a global capability check rather than a namespace-aware one, this is a deliberate design choice in the Linux kernel to prevent resource exhaustion across containers.

The code correctly implements bounds checking using array_index_nospec to prevent speculative out-of-bounds access (Spectre v1). It also enforces a strict permission check (capable(CAP_SYS_RESOURCE)) which, while restrictive for user namespaces, ensures that only globally privileged users can increase hard resource limits, preventing unprivileged users or containers from exceeding system-wide resource boundaries. The locking mechanism (task_lock) ensures atomicity within the task group leader context.

Resource limits can only be increased if the caller has global CAP_SYS_RESOURCE.

Resource indices must never result in out-of-bounds memory access, even under speculative execution.

Soft limits (rlim_cur) cannot exceed hard limits (rlim_max).

The check 'new_rlim->rlim_max > rlim->rlim_max && !capable(CAP_SYS_RESOURCE)' enforces the privilege invariant.

array_index_nospec(resource, RLIM_NLIMITS) verifies the bounds invariant against speculation.

The check 'new_rlim->rlim_cur > new_rlim->rlim_max' enforces the limit consistency invariant.

There is a known architectural limitation where rlimits are not yet fully namespaced/cgrouped, leading to the use of global 'capable()' instead of 'ns_capable()'. This is a functional limitation for containers rather than a security vulnerability.


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/document/document.php
Function: document_listing_display

Call path: document.php (main/document/document.php) → DocumentManager::get_document_data_by_id (main/document/document.php) → document_listing_display (main/document/document.php)

### Primary Function

```php
<?php
// Document title with link and comment
$titleWithLink = $link.$session_img.'<br />'.$invisibility_span_open;
$commentText = nl2br(htmlspecialchars($document_data['comment'], ENT_QUOTES, $charset));
if (!empty($commentText)) {
    $titleWithLink .= '<em>'.$commentText.'</em>';
}
$titleWithLink .= $invisibility_span_close.$user_link;
$row[] = $titleWithLink;
```

### Cross-File Context

[Security::remove_XSS — method — main/inc/lib/security.lib.php:203]
public static function remove_XSS($var, $user_status = null, $filter_terms = false) { if ($filter_terms) { $var = self::filter_terms($var); } if (empty($user_status)) { if (api_is_anonymous()) { $user_status = ANONYMOUS; } else { if (api_is_allowed_to_edit()) { $user_status = COURSEMANAGER; } else { $user_status = STUDENT; } } } if ($user_status == COURSEMANAGERLOWSECURITY) { return $var; } static $purifier = []; if (!isset($purifier[$user_status])) { $cache_dir = api_get_path(SYS_ARCHIVE_PATH).'Serializer'; if (!file_exists($cache_dir)) { $mode = api_get_permissions_for_new_directories(); mkdir($cache_dir, $mode); } $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.SerializerPath', $cache_dir); $config->set('Core.Encoding', api_get_system_encoding()); $config->set('HTML.Doctype', 'XHTML 1.0 Transitional'); $config->set('HTML.MaxImgLength', '2560'); $config->set('HTML.TidyLevel', 'light'); $config->set('Core.ConvertDocumentToFragment', false); $config->set('Core.RemoveProcessingInstructions', true); if (api_get_setting('enable_iframe_inclusion') == 'true') { $config->set('Filter.Custom', [new AllowIframes()]); } $config->set('Attr.AllowedFrameTargets', ['_blank', '_top', '_self', '_parent']); if ($user_status == STUDENT) { global $allowed_html_student; $config->set('HTML.SafeEmbed', true); $config->set('HTML.SafeObject', true); $config->set('Filter.YouTube', true); $config->set('HTML.FlashAllowFullScreen', true); $config->set('HTML.Allowed', $allowed_html_student); } elseif ($user_status == COURSEMANAGER) { global $allowed_html_teacher; $config->set('HTML.SafeEmbed', true); $config->set('HTML.SafeObject', true); $config->set('Filter.YouTube', true); $config->set('HTML.FlashAllowFullScreen', true); $config->set('HTML.Allowed', $allowed_html_teacher); } else { global $allowed_html_anonymous; $config->set('HTML.Allowed', $allowed_html_anonymous); } $config->set('Attr.EnableID', true); $config->set('CSS.AllowImportant', true); $config->set('CSS.AllowTricky', true); $config->set('CSS.Proprietary', true); $config->set('URI.AllowedSchemes', ['http' => true, 'https' => true, 'mailto' => true, 'ftp' => true, 'nntp' => true, 'news' => true, 'data' => true]); $config->set('HTML.SafeIframe', true); $config->set('HTML.DefinitionID', 'html5-definitions'); $config->set('HTML.DefinitionRev', 1); if ($def = $config->maybeGetRawHTMLDefinition()) { $def->addElement('video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', ['src' => 'URI', 'type' => 'Text', 'width' => 'Length', 'height' => 'Length', 'poster' => 'URI', 'preload' => 'Enum#auto,metadata,none', 'controls' => 'Bool']); $def->addElement('audio', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', ['autoplay' => 'Bool', 'src' => 'URI', 'loop' => 'Bool', 'preload' => 'Enum#auto,metadata,none', 'controls' => 'Bool', 'muted' => 'Bool']); $def->addElement('source', 'Block', 'Flow', 'Common', ['src' => 'URI', 'type' => 'Text']); } $purifier[$user_status] = new HTMLPurifier($config); } if (is_array($var)) { return $purifier[$user_status]->purifyArray($var); } else { return $purifier[$user_status]->purify($var); } }

Control flow: User requests document listing -> document.php retrieves document data via DocumentManager -> builds HTML title with link and comment -> outputs to browser without sanitization -> XSS executes in victim's browser

Data flow: Document title (user-controlled) -> $link variable -> $titleWithLink concatenation -> $row[] array -> HTML output -> Browser execution

Verdict: Vulnerable
Vulnerable region:
[main/document/document.php]
```php
);

            // Document title with link and comment
            $titleWithLink = $link.$session_img.'<br />'.$invisibility_span_open;
            $commentText = nl2br(htmlspecialchars($document_data['comment'], ENT_QUOTES, $charset));
            if (!empty($commentText)) {
                $titleWithLink .= '<em>'.$commentText.'</em>';
            }
```
CWE: CWE-79
Severity: MEDIUM

Root cause: The $link variable containing the document title HTML is assigned directly to $titleWithLink without passing through Security::remove_XSS() sanitization. The $link variable is constructed from document data including the title which can be set/modified by users with document creation/editing permissions, allowing injection of malicious HTML/script tags that execute when the document list is displayed.

Exploitability: An attacker with course document creation/edit permissions creates or renames a document with a malicious JavaScript payload in the title. When a user views the course document listing, the JavaScript executes in their browser.

Attack preconditions:
- Attacker must have ability to create or rename documents in a course (authenticated user with document creation/edit permissions)
- Attacker crafts document title containing malicious HTML/JavaScript
- Victim user views the affected document listing page

Likely impact: An attacker with course document creation/edit permissions creates or renames a document with a malicious JavaScript payload in the title. When a user views the course document listing, the JavaScript executes in their browser.

);

            // Document title with link and comment
            $titleWithLink = $link.$session_img.'
'.$invisibility_span_open;
            $commentText = nl2br(htmlspecialchars($document_data['comment'], ENT_QUOTES, $charset));
            if (!empty($commentText)) {
                $titleWithLink .= ''.$commentText.'';
            }

Based on the provided code snippet and the context of the Chamilo LMS `document.php` file, here is an audit of the potential vulnerability.

### Analysis

The code snippet focuses on constructing a display string (`$titleWithLink`) for a document listing, which includes a link, an icon, an optional comment, and user information.

1.  **Sanitization of `$commentText`**:
    The variable `$commentText` is derived from `$document_data['comment']`. It is processed using `htmlspecialchars($document_data['comment'], ENT_QUOTES, $charset)`. This effectively neutralizes XSS vectors (like `<script>` tags or malicious attributes) within the comment field by converting special characters into HTML entities.

2.  **Potential Vulnerability in `$link` and `$user_link`**:
    The variable `$titleWithLink` is constructed by concatenating `$link`, `$session_img`, `$invisibility_span_open`, `$commentText`, `$invisibility_span_close`, and `$user_link`.

    In the context of Chamilo's `document_listing_display` function (though not fully shown in the snippet), the `$link` variable typically contains an anchor tag (`<a>...</a>`) wrapping the **document title**. If the document title is sourced from user input and is not escaped before being placed into the `$link` string, an XSS vulnerability exists.

    Similarly, `$user_link` often contains the name of the user who uploaded the document. If the username is not escaped, it could also serve as a vector.

3.  **The Provided Context**:
    The inclusion of `Security::remove_XSS` in the context suggests that the application has a mechanism for sanitizing HTML. However, in the provided snippet, `remove_XSS` is **not called**. While `htmlspecialchars` is used for the comment, the other variables (`$link`, `$user_link`) are appended directly to the output.

### Conclusion

The specific lines provided for `$commentText` are **safe** because they use `htmlspecialchars`.

However, the overall construction of `$titleWithLink` presents a **high risk of a "Stored XSS" vulnerability** if the upstream variables `$link` or `$user_link` (which are not shown as sanitized in this snippet) contain unescaped user-controlled data such as a document's title or a user's name. In Chamilo LMS, document titles are known to be user-definable, and if the logic forming `$link` simply concatenates the title into an HTML string, an attacker could name a file `<img src=x onerror=alert(1)>` to execute arbitrary JavaScript in the browser of any user viewing the document list.

**Status: Vulnerable (Contextual)**
The snippet itself shows proper escaping for the *comment*, but the architectural pattern of concatenating raw HTML strings (`$link . $user_link`) without a final sanitization step or ensuring all components are escaped is a classic source of XSS in Chamilo. If `$link` contains an unescaped title, the code is vulnerable.

The code constructs an HTML string by concatenating variables ($link, $user_link) that contain user-provided data (like document titles) without consistently applying sanitization (e.g., htmlspecialchars or Security::remove_XSS). While the 'comment' field is properly escaped, the preceding and following variables are not, allowing Stored XSS.

High. A user with permissions to upload or rename documents can set a document title containing a malicious payload like '<script>alert(1)</script>'. This will execute in the context of any user viewing the document list.

The attacker must have the ability to upload a file or rename a document within a course where they have sufficient privileges (e.g., student or teacher).

Execution of arbitrary JavaScript in the victim's browser, leading to session hijacking, credential theft, or unauthorized actions on behalf of the user.

All user-controllable data must be sanitized or escaped at the point of output to prevent execution of malicious HTML/JavaScript.

Checking if all variables concatenated into $titleWithLink are either hardcoded or passed through an escaping function. In this case, $link and $user_link are derived from user-controlled titles and names and are not escaped in the provided snippet.

The code uses htmlspecialchars for the comment but fails to apply the same standard to the title ($link), which is a common oversight in legacy PHP applications.


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/usb/typec/tcpm/tcpm.c
Function: tcpm_unregister_port

Call path: tcpm_unregister_port (drivers/usb/typec/tcpm/tcpm.c) → kthread_destroy_worker (kernel/kthread.c) → state_machine_timer_handler (drivers/usb/typec/tcpm/tcpm.c) → vdm_state_machine_timer_handler (drivers/usb/typec/tcpm/tcpm.c) → kthread_queue_work (kernel/kthread.c)

### Primary Function

```c
void tcpm_unregister_port(struct tcpm_port *port)
{
	int i;

	tcpm_reset_port(port);
	for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++)
		typec_unregister_altmode(port->port_altmode[i]);
	typec_unregister_port(port->typec_port);
	usb_role_switch_put(port->role_sw);
	tcpm_debugfs_exit(port);
	kthread_destroy_worker(port->wq);
}
```

### Cross-File Context

[state_machine_timer_handler — callee — drivers/usb/typec/tcpm/tcpm.c:5069]
```c
static enum hrtimer_restart state_machine_timer_handler(struct hrtimer *timer)
{
	struct tcpm_port *port = container_of(timer, struct tcpm_port, state_machine_timer);

	kthread_queue_work(port->wq, &port->state_machine);
	return HRTIMER_NORESTART;
}
```

[vdm_state_machine_timer_handler — callee — drivers/usb/typec/tcpm/tcpm.c:5077]
```c
static enum hrtimer_restart vdm_state_machine_timer_handler(struct hrtimer *timer)
{
	struct tcpm_port *port = container_of(timer, struct tcpm_port, vdm_state_machine_timer);

	kthread_queue_work(port->wq, &port->vdm_state_machine);
	return HRTIMER_NORESTART;
}
```

[state_machine_timer — field — drivers/usb/typec/tcpm/tcpm.c:301]
struct hrtimer state_machine_timer;

[vdm_state_machine_timer — field — drivers/usb/typec/tcpm/tcpm.c:303]
struct hrtimer vdm_state_machine_timer;

[hrtimer_cancel — function — include/linux/hrtimer.h]
```c
int hrtimer_cancel(struct hrtimer *timer);
```

[kthread_destroy_worker — caller — kernel/kthread.c]
```c
void kthread_destroy_worker(struct kthread_worker *worker);
```

[kthread_queue_work — sink — kernel/kthread.c]
```c
bool kthread_queue_work(struct kthread_worker *worker, struct kthread_work *work);
```

Control flow: tcpm_unregister_port calls tcpm_reset_port, unregisters altmodes/port, puts role switch, exits debugfs, then destroys kthread_worker. Without canceling timers first, any pending timer callback will try to queue work on the already-destroyed worker.

Data flow: port->wq pointer is passed to kthread_destroy_worker and later used in timer handlers via container_of. Timer handlers receive port pointer from timer struct, then call kthread_queue_work(port->wq, ...). After worker destruction, port->wq is freed but timer handlers still hold reference to it.

Verdict: Vulnerable
Vulnerable region:
[drivers/usb/typec/tcpm/tcpm.c]
```c
void tcpm_unregister_port(struct tcpm_port *port)
{
	int i;

	tcpm_reset_port(port);
	for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++)
		typec_unregister_altmode(port->port_altmode[i]);
	typec_unregister_port(port->typec_port);
	usb_role_switch_put(port->role_sw);
	tcpm_debugfs_exit(port);
	kthread_destroy_worker(port->wq);
}
EXPORT_SYMBOL_GPL(tcpm_unregister_port);

MODULE_AUTHOR("Guenter Roeck <groeck@chromium.org>");
MODULE_DESCRIPTION("USB Type-C Port Manager");
```
CWE: CWE-416
Severity: MEDIUM

Root cause: In tcpm_unregister_port (pre-fix), hrtimer_cancel calls for port->state_machine_timer and port->vdm_state_machine_timer were missing before kthread_destroy_worker(port->wq). When these timers fired after worker destruction, kthread_queue_work accessed freed worker memory, causing kernel paging request/oops.

Exploitability: Triggered by unloading the tcpci module (or any tcpm-based driver) while timers are pending. The timers will fire after worker destruction, accessing freed kthread_worker.

Attack preconditions:
- tcpm port registered with active timers
- module unload or tcpm_unregister_port called while timers pending

Likely impact: Triggered by unloading the tcpci module (or any tcpm-based driver) while timers are pending. The timers will fire after worker destruction, accessing freed kthread_worker.

The provided code for `tcpm_unregister_port` contains a **Race Condition** leading to a **Use-After-Free (UAF)** or **Null Pointer Dereference**.

### Analysis:
1.  **Timer Persistence**: The `tcpm_port` structure maintains two high-resolution timers: `state_machine_timer` and `vdm_state_machine_timer`. These timers are used to schedule work on the `port->wq` kthread worker.
2.  **Missing Cancellation**: In `tcpm_unregister_port`, the code performs several cleanup actions, including `kthread_destroy_worker(port->wq)`. However, it fails to call `hrtimer_cancel()` for the two timers.
3.  **The Race**:
    *   `tcpm_unregister_port` is called.
    *   `kthread_destroy_worker(port->wq)` is executed. This function stops the worker thread and flushes existing work, but it does not prevent timers from firing in the future.
    *   If a timer (e.g., `state_machine_timer`) was scheduled before the unregistration and expires **after** or **during** the destruction of the worker, the handler `state_machine_timer_handler` will execute.
    *   The handler calls `kthread_queue_work(port->wq, &port->state_machine)`.
4.  **Vulnerability**:
    *   **Use-After-Free**: If the memory for `port->wq` has been deallocated (or is in the process of being cleaned up), `kthread_queue_work` will access invalid memory.
    *   **Kernel Panic**: Even if the worker memory hasn't been freed yet, `kthread_queue_work` typically accesses `worker->task`. If `kthread_destroy_worker` has already nullified this or cleaned up the associated task, it leads to a crash.

### Call Path Visualization:
```
tcpm_unregister_port(port)
  -> kthread_destroy_worker(port->wq)
     [ Timer expires ]
     -> state_machine_timer_handler(timer)
        -> kthread_queue_work(port->wq, ...)  <-- UAF/Dereference of destroyed worker
```

### Recommendation:
To fix this, `hrtimer_cancel()` must be called for both `state_machine_timer` and `vdm_state_machine_timer` before destroying the kthread worker. This ensures that no further work can be queued by the timer handlers during or after the destruction process.

```c
void tcpm_unregister_port(struct tcpm_port *port)
{
    // ... cleanup ...
    hrtimer_cancel(&port->state_machine_timer);
    hrtimer_cancel(&port->vdm_state_machine_timer);
    kthread_destroy_worker(port->wq);
}
```

This vulnerability is a classic example of improper teardown order in the Linux kernel, where asynchronous callbacks (timers) are not synchronized with the destruction of the resources they depend on (the kthread worker).

void tcpm_unregister_port(struct tcpm_port *port)
{
	// ...
	kthread_destroy_worker(port->wq);
}

The function fails to cancel pending hrtimers (state_machine_timer and vdm_state_machine_timer) before destroying the kthread_worker they depend on. This creates a race condition where a timer can expire after the worker is destroyed, causing the timer handler to attempt to queue work to a freed or invalid worker structure.

Exploiting this requires precise timing where a TCPM state machine event is scheduled to trigger exactly during the port unregistration process. While difficult to trigger reliably for a targeted attack, it is a frequent source of stability issues and kernel panics in production environments.

The TCPM port must be in a state where a state machine or VDM timer is active while the driver is being unbinded or the port is being unregistered.

Kernel panic (Null Pointer Dereference or Use-After-Free) leading to Denial of Service (DoS).

All asynchronous callbacks (timers, interrupts) must be synchronized or cancelled before the data structures and execution contexts (workers) they reference are deallocated.

Checking the call path shows that tcpm_unregister_port leads to kthread_destroy_worker without calling hrtimer_cancel on the associated timers defined in the struct tcpm_port.

A fix requires adding hrtimer_cancel() for both state_machine_timer and vdm_state_machine_timer before the kthread_destroy_worker call.

tcpm_unregister_port -> kthread_destroy_worker -> [Async Timer Event] -> state_machine_timer_handler -> kthread_queue_work


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/torvalds/linux
Language: C
File: drivers/scsi/scsi_transport_iscsi.c
Function: iscsi_set_param

Call path: iscsi_if_rx (drivers/scsi/scsi_transport_iscsi.c) → iscsi_if_recv_msg (drivers/scsi/scsi_transport_iscsi.c) → iscsi_set_param (drivers/scsi/scsi_transport_iscsi.c) → transport->set_param (drivers/scsi/libiscsi.c) → iscsi_session_get_param (drivers/scsi/libiscsi.c) → iscsi_conn_get_param (drivers/scsi/libiscsi.c) → iscsi_host_get_param (drivers/scsi/libiscsi.c)

### Primary Function

```c
static int
iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
{
	char *data = (char*)ev + sizeof(*ev);
	struct iscsi_cls_conn *conn;
	struct iscsi_cls_session *session;
	int err = 0, value = 0;

	if (ev->u.set_param.len > PAGE_SIZE)
		return -EINVAL;

	session = iscsi_session_lookup(ev->u.set_param.sid);
	conn = iscsi_conn_lookup(ev->u.set_param.sid, ev->u.set_param.cid);
	if (!conn || !session)
		return -EINVAL;

	switch (ev->u.set_param.param) {
	case ISCSI_PARAM_SESS_RECOVERY_TMO:
		sscanf(data, "%d", &value);
		if (!session->recovery_tmo_sysfs_override)
			session->recovery_tmo = value;
		break;
	default:
		err = transport->set_param(conn, ev->u.set_param.param,
					   data, ev->u.set_param.len);
	}

	return err;
}
```

### Cross-File Context

[iscsi_set_host_param — function — drivers/scsi/scsi_transport_iscsi.c:3026]
```c
static int
iscsi_set_host_param(struct iscsi_transport *transport,
		     struct iscsi_uevent *ev)
{
	char *data = (char*)ev + sizeof(*ev);
	struct Scsi_Host *shost;
	int err;

	if (!transport->set_host_param)
		return -ENOSYS;

	if (ev->u.set_host_param.len > PAGE_SIZE)
		return -EINVAL;

	shost = scsi_host_lookup(ev->u.set_host_param.host_no);
	if (!shost) {
		printk(KERN_ERR "set_host_param could not find host no %u\n",
		       ev->u.set_host_param.host_no);
		return -ENODEV;
	}

	err = transport->set_host_param(shost, ev->u.set_host_param.param,
					data, ev->u.set_host_param.len);
	scsi_host_put(shost);
	return err;
}
```

[sysfs_emit — function — fs/sysfs/file.c:732]
```c
int sysfs_emit(char *buf, const char *fmt, ...)
{
	va_list args;
	int len;

	if (WARN(!buf || offset_in_page(buf),
		 "invalid sysfs_emit: buf:%p\n", buf))
		return 0;

	va_start(args, fmt);
	len = vscnprintf(buf, PAGE_SIZE, fmt, args);
	va_end(args);

	return len;
}
```

[PAGE_SIZE — constant — arch/x86/include/asm/page_size.h:4]
PAGE_SIZE → 4096  (arch/x86/include/asm/page_size.h:4)

[struct iscsi_uevent — struct — include/scsi/iscsi_if.h:93]
```c
struct iscsi_uevent {
	uint32_t type;
	uint32_t iferror;
	uint64_t transport_handle;

	union {
		struct msg_set_param {
			uint32_t sid;
			uint32_t cid;
			uint32_t param;
			uint32_t len;
		} set_param;
		/* ... other fields ... */
	} u;
}
```

[enum iscsi_param — enum — include/scsi/iscsi_if.h:526]
enum iscsi_param { ISCSI_PARAM_MAX_RECV_DLENGTH, ISCSI_PARAM_MAX_XMIT_DLENGTH, /* ... */ ISCSI_PARAM_USERNAME, ISCSI_PARAM_PASSWORD, ISCSI_PARAM_TARGET_NAME, /* ... */ ISCSI_PARAM_MAX, };

Control flow: The function extracts a data pointer from the uevent structure and immediately validates the user-controlled length field `ev->u.set_param.len` against `PAGE_SIZE`, returning `-EINVAL` if the limit is exceeded. It then performs kernel object lookups for the session and connection, returning `-EINVAL` if either lookup fails. Execution branches based on `ev->u.set_param.param`: for `ISCSI_PARAM_SESS_RECOVERY_TMO`, it parses an integer via `sscanf` and conditionally updates a session timeout field; for all other parameters, it delegates to `transport->set_param`. The function concludes by returning the accumulated error code.

Data flow: The input `ev` structure provides the payload length (`ev->u.set_param.len`), parameter identifier, and session/connection IDs. `data` is derived as a pointer to the memory region immediately following the `ev` header. The length field is validated early. In the recovery timeout case, `data` is parsed into a local integer `value`. In the default case, `data` and the validated `len` are forwarded to the transport callback. The only state modifications occur on the `session` struct or are passed through to the callee, with all external inputs bounded before consumption.

Verdict: Not Vulnerable

Reviewed region:
[drivers/scsi/scsi_transport_iscsi.c]
```c
static int
iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
{
	char *data = (char*)ev + sizeof(*ev);
	struct iscsi_cls_conn *conn;
	struct iscsi_cls_session *session;
	int err = 0, value = 0;

	if (ev->u.set_param.len > PAGE_SIZE)
		return -EINVAL;

	session = iscsi_session_lookup(ev->u.set_param.sid);
	conn = iscsi_conn_lookup(ev->u.set_param.sid, ev->u.set_param.cid);
	if (!conn || !session)
		return -EINVAL;

	switch (ev->u.set_param.param) {
	case ISCSI_PARAM_SESS_RECOVERY_TMO:
		sscanf(data, "%d", &value);
		if (!session->recovery_tmo_sysfs_override)
			session->recovery_tmo = value;
		break;
	default:
		err = transport->set_param(conn, ev->u.set_param.param,
					   data, ev->u.set_param.len);
	}

	return err;
}
```

Why it is not vulnerable: The code explicitly validates the user-controlled `ev->u.set_param.len` against `PAGE_SIZE` before any parsing, state modification, or callback delegation occurs. This early bounds check prevents oversized payloads from triggering buffer overflows or memory corruption. Additionally, mandatory null checks on `session` and `conn` prevent invalid pointer dereferences, and the format string used for integer parsing is strict and safe.

Security invariants:
- User-controlled payload length must not exceed `PAGE_SIZE` before any processing. Enforced by `if (ev->u.set_param.len > PAGE_SIZE) return -EINVAL;`.
- Kernel object pointers must be valid before access or delegation. Enforced by `if (!conn || !session) return -EINVAL;`.
- Downstream callbacks must receive a validated length to prevent unbounded reads/writes. Enforced by passing `ev->u.set_param.len` to `transport->set_param` only after the `PAGE_SIZE` gate.
- String-to-integer parsing must be bounded and use safe formats. Enforced by the `PAGE_SIZE` upper limit on `data` and the strict `"%d"` format specifier in `sscanf`.

Invariant verification:
- Length bounds validation: holds=true. Evidence: The condition `if (ev->u.set_param.len > PAGE_SIZE) return -EINVAL;` executes immediately after data extraction, rejecting oversized inputs before any usage.
- Null pointer safety: holds=true. Evidence: The lookup results are checked via `if (!conn || !session) return -EINVAL;` prior to dereferencing `session->recovery_tmo_sysfs_override` or passing `conn` to the callback.
- Safe payload delegation: holds=true. Evidence: The validated `len` is explicitly passed to `transport->set_param(conn, ev->u.set_param.param, data, ev->u.set_param.len)`, ensuring downstream handlers operate on a strictly bounded size.

Residual risk notes: The `sscanf` call for `ISCSI_PARAM_SESS_RECOVERY_TMO` does not explicitly use the `len` field to limit parsing, relying instead on the payload's implicit null-termination and the `PAGE_SIZE` upper bound. While acceptable given the strict length gate, using size-aware parsing would be marginally more robust. Additionally, the safety of the `default` branch inherently relies on `transport->set_param` correctly respecting the `len` parameter it receives.

The function `iscsi_set_param` in `drivers/scsi/scsi_transport_iscsi.c` contains a vulnerability related to improper string handling and memory access.

### Analysis

The vulnerability exists in how the function handles the `data` buffer received from a Netlink message (`struct iscsi_uevent *ev`).

1.  **Missing Null-Termination:**
    The code calculates the data pointer as `char *data = (char*)ev + sizeof(*ev)`. The length of this data is supposedly defined by `ev->u.set_param.len`. However, the code uses `sscanf(data, "%d", &value)` when the parameter is `ISCSI_PARAM_SESS_RECOVERY_TMO`.
    The `sscanf` function expects a null-terminated string. If a malicious or malformed Netlink message is sent where the `data` portion is not null-terminated within the bounds of the allocated buffer, `sscanf` will perform an **out-of-bounds read**, continuing until it encounters a null byte or triggers a page fault.

2.  **Insufficient Validation of `len`:**
    While there is a check `if (ev->u.set_param.len > PAGE_SIZE)`, this only validates the metadata. It does not ensure that the actual memory buffer containing `ev` is large enough to hold `sizeof(*ev) + ev->u.set_param.len`. If the Netlink message payload is smaller than what `len` claims, the subsequent `transport->set_param` call (which uses `data` and `len`) will also perform an out-of-bounds read from kernel memory.

3.  **Impact on Drivers:**
    In the `default` case, the code calls `transport->set_param(conn, ev->u.set_param.param, data, ev->u.set_param.len)`. Many iSCSI transport drivers (like `libiscsi`) assume that for string-based parameters (e.g., `ISCSI_PARAM_TARGET_NAME`), the `data` buffer is a valid, null-terminated string of the specified length. Because `iscsi_set_param` does not enforce null-termination at `data[len-1]`, this can lead to buffer overflows or further out-of-bounds reads in various driver-specific implementations.

### Vulnerability Type
**Buffer Over-read / Missing Null Termination**

### Risk Assessment
*   **Confidentiality:** High. An attacker can potentially leak kernel memory contents by setting iSCSI parameters and then reading them back via sysfs or other iSCSI query mechanisms.
*   **Availability:** Medium. Out-of-bounds reads can lead to kernel panics (DoS) if they hit unmapped memory.
*   **Privilege Required:** Typically requires `CAP_SYS_ADMIN` or equivalent to send iSCSI Netlink messages, though this depends on the specific distribution's Netlink socket permissions.

### Recommendation
The code should explicitly null-terminate the `data` buffer before processing it. A common fix in the kernel for this pattern is:
```c
/* Ensure the length is within bounds and the string is null-terminated */
if (ev->u.set_param.len > PAGE_SIZE)
    return -EINVAL;

data[ev->u.set_param.len - 1] = '\0'; // Ensure termination
```
Additionally, the caller of `iscsi_set_param` must ensure that the Netlink message size actually matches `sizeof(*ev) + ev->u.set_param.len`.

The function calculates a pointer to 'data' from a Netlink message buffer but fails to ensure that the data is null-terminated before passing it to string-processing functions like sscanf. Additionally, while it checks if the reported length is within PAGE_SIZE, it does not verify that the actual buffer provided in the Netlink message is large enough to contain the claimed length, potentially leading to out-of-bounds reads.

Exploitable by a user with permissions to send Netlink messages to the iSCSI subsystem (typically requiring CAP_SYS_ADMIN).

The attacker must have the ability to send iSCSI Netlink messages to the kernel.

An out-of-bounds read can lead to a kernel information leak (if the data is later reflected back to userspace) or a system crash (denial of service) if the read hits unmapped memory.

Data buffers received from userspace over Netlink must be validated for actual size and, if processed as strings, must be explicitly null-terminated within the bounds of the allocated buffer.

The code fails the invariant by trusting ev->u.set_param.len without verifying the underlying skb buffer length and by calling sscanf on a buffer that is not guaranteed to be null-terminated.

The vulnerability is mitigated by the fact that it usually requires administrative privileges to interact with the iSCSI Netlink interface.

iscsi_if_rx -> iscsi_if_recv_msg -> iscsi_set_param