From: qianjiaru A reference counting management vulnerability exists in the Linux kernel's XFRM (IPsec Transform) policy subsystem. Based on variant analysis of CVE-2022-36879, this vulnerability involves improper policy object lifecycle management in the `__xfrm_policy_check()` function, potentially leading to double free conditions and system instability. ## Vulnerability Mechanism The issue follows the same pattern as CVE-2022-36879 but in a different code path: 1. **Policy Reference Acquired**: `pols[0]` contains a valid policy with acquired reference 2. **Secondary Lookup Fails**: `xfrm_policy_lookup_bytype()` returns error for `pols[1]` 3. **Partial Cleanup**: Error path calls `xfrm_pol_put(pols[0])` but doesn't clear policy array state 4. **Caller Confusion**: Calling function may not be aware that `pols[0]` has already been released 5. **Double Release Risk**: If caller attempts cleanup based on incomplete state information ## Comparison with CVE-2022-36879 **CVE-2022-36879 (Fixed)**: `xfrm_expand_policies()` had double reference counting issues ```c // Original vulnerability pattern (now fixed): if (IS_ERR(pols[1])) { xfrm_pols_put(pols, *num_pols); // Released references return PTR_ERR(pols[1]); // But didn't set *num_pols = 0 } ``` **This Variant**: Similar reference management issues in different function ```c // Current potential issue: if (IS_ERR(pols[1])) { xfrm_pol_put(pols[0]); // Releases pols[0] return 0; // But pols[0] pointer remains unchanged } ``` ## Attack Scenario 1. **Policy Lookup**: Network packet triggers `__xfrm_policy_check()` with sub-policy configuration 2. **Primary Policy Found**: `pols[0]` gets a valid policy reference 3. **Secondary Lookup Fails**: `xfrm_policy_lookup_bytype()` fails, returns error in `pols[1]` 4. **Partial Cleanup**: Function releases `pols[0]` reference but doesn't clear the pointer 5. **Caller Misunderstanding**: Calling function may attempt to use or release `pols[0]` again 6. **Memory Corruption**: Double free or use-after-free leads to system instability ## Proposed Fix The vulnerability should be fixed by ensuring complete state cleanup in error paths: ```c // Current potentially vulnerable code: if (IS_ERR(pols[1])) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); xfrm_pol_put(pols[0]); return 0; } // Proposed secure fix: if (IS_ERR(pols[1])) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); xfrm_pol_put(pols[0]); pols[0] = NULL; // Clear pointer to prevent reuse return 0; } ``` ### Alternative Comprehensive Fix If the calling context requires more extensive cleanup: ```c if (IS_ERR(pols[1])) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); xfrm_pol_put(pols[0]); memset(pols, 0, sizeof(pols)); // Clear entire policy array npols = 0; // Reset policy count return 0; } ``` ## References - **Original CVE**: CVE-2022-36879 (xfrm_expand_policies double free) - **Linux XFRM Documentation**: `Documentation/networking/xfrm_*.txt` - **XFRM Source**: `net/xfrm/xfrm_policy.c` - **IPsec RFCs**: RFC 4301, RFC 4306 Signed-off-by: qianjiaru --- net/xfrm/xfrm_policy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index c5035a9bc..50943fa4e 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -3786,6 +3786,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, if (IS_ERR(pols[1])) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); xfrm_pol_put(pols[0]); + pols[0] = NULL; // Clear pointer to prevent reuse return 0; } /* This write can happen from different cpus. */ -- 2.34.1