VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-46042

CVE-2026-46042

Description

In the Linux kernel, the following vulnerability has been resolved:

mm/mempolicy: fix memory leaks in weighted_interleave_auto_store()

weighted_interleave_auto_store() fetches old_wi_state inside the if (!input) block only. This causes two memory leaks:

1. When a user writes "false" and the current mode is already manual, the function returns early without freeing the freshly allocated new_wi_state.

2. When a user writes "true", old_wi_state stays NULL because the fetch is skipped entirely. The old state is then overwritten by rcu_assign_pointer() but never freed, since the cleanup path is gated on old_wi_state being non-NULL. A user can trigger this repeatedly by writing "1" in a loop.

Fix both leaks by moving the old_wi_state fetch before the input check, making it unconditional. This also allows a unified early return for both "true" and "false" when the requested mode matches the current mode.

Reviewed by: Donet Tom <donettom@linux.ibm.com>

Affected products

2

Patches

6
39caa9ca863f

mm/mempolicy: fix memory leaks in weighted_interleave_auto_store()

1 file changed · +12 12
  • mm/mempolicy.c+12 12 modified
    diff --git a/mm/mempolicy.c b/mm/mempolicy.c
    index cf92bd6a8226ee..ebe4bc8220b14e 100644
    --- a/mm/mempolicy.c
    +++ b/mm/mempolicy.c
    @@ -3706,18 +3706,19 @@ static ssize_t weighted_interleave_auto_store(struct kobject *kobj,
     		new_wi_state->iw_table[i] = 1;
     
     	mutex_lock(&wi_state_lock);
    -	if (!input) {
    -		old_wi_state = rcu_dereference_protected(wi_state,
    -					lockdep_is_held(&wi_state_lock));
    -		if (!old_wi_state)
    -			goto update_wi_state;
    -		if (input == old_wi_state->mode_auto) {
    -			mutex_unlock(&wi_state_lock);
    -			return count;
    -		}
    +	old_wi_state = rcu_dereference_protected(wi_state,
    +				lockdep_is_held(&wi_state_lock));
     
    -		memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    -					       nr_node_ids * sizeof(u8));
    +	if (old_wi_state && input == old_wi_state->mode_auto) {
    +		mutex_unlock(&wi_state_lock);
    +		kfree(new_wi_state);
    +		return count;
    +	}
    +
    +	if (!input) {
    +		if (old_wi_state)
    +			memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    +						       nr_node_ids * sizeof(u8));
     		goto update_wi_state;
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
6fae274ce0e3

mm/mempolicy: fix memory leaks in weighted_interleave_auto_store()

1 file changed · +12 12
  • mm/mempolicy.c+12 12 modified
    diff --git a/mm/mempolicy.c b/mm/mempolicy.c
    index fd08771e2057b3..62108a5b74c4ed 100644
    --- a/mm/mempolicy.c
    +++ b/mm/mempolicy.c
    @@ -3700,18 +3700,19 @@ static ssize_t weighted_interleave_auto_store(struct kobject *kobj,
     		new_wi_state->iw_table[i] = 1;
     
     	mutex_lock(&wi_state_lock);
    -	if (!input) {
    -		old_wi_state = rcu_dereference_protected(wi_state,
    -					lockdep_is_held(&wi_state_lock));
    -		if (!old_wi_state)
    -			goto update_wi_state;
    -		if (input == old_wi_state->mode_auto) {
    -			mutex_unlock(&wi_state_lock);
    -			return count;
    -		}
    +	old_wi_state = rcu_dereference_protected(wi_state,
    +				lockdep_is_held(&wi_state_lock));
     
    -		memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    -					       nr_node_ids * sizeof(u8));
    +	if (old_wi_state && input == old_wi_state->mode_auto) {
    +		mutex_unlock(&wi_state_lock);
    +		kfree(new_wi_state);
    +		return count;
    +	}
    +
    +	if (!input) {
    +		if (old_wi_state)
    +			memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    +						       nr_node_ids * sizeof(u8));
     		goto update_wi_state;
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
c42a7efb9060

mm/mempolicy: fix memory leaks in weighted_interleave_auto_store()

1 file changed · +12 12
  • mm/mempolicy.c+12 12 modified
    diff --git a/mm/mempolicy.c b/mm/mempolicy.c
    index 94327574fbbbbf..779a8cc17a8324 100644
    --- a/mm/mempolicy.c
    +++ b/mm/mempolicy.c
    @@ -3636,18 +3636,19 @@ static ssize_t weighted_interleave_auto_store(struct kobject *kobj,
     		new_wi_state->iw_table[i] = 1;
     
     	mutex_lock(&wi_state_lock);
    -	if (!input) {
    -		old_wi_state = rcu_dereference_protected(wi_state,
    -					lockdep_is_held(&wi_state_lock));
    -		if (!old_wi_state)
    -			goto update_wi_state;
    -		if (input == old_wi_state->mode_auto) {
    -			mutex_unlock(&wi_state_lock);
    -			return count;
    -		}
    +	old_wi_state = rcu_dereference_protected(wi_state,
    +				lockdep_is_held(&wi_state_lock));
     
    -		memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    -					       nr_node_ids * sizeof(u8));
    +	if (old_wi_state && input == old_wi_state->mode_auto) {
    +		mutex_unlock(&wi_state_lock);
    +		kfree(new_wi_state);
    +		return count;
    +	}
    +
    +	if (!input) {
    +		if (old_wi_state)
    +			memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    +						       nr_node_ids * sizeof(u8));
     		goto update_wi_state;
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
39caa9ca863f

mm/mempolicy: fix memory leaks in weighted_interleave_auto_store()

1 file changed · +12 12
  • mm/mempolicy.c+12 12 modified
    diff --git a/mm/mempolicy.c b/mm/mempolicy.c
    index cf92bd6a8226ee..ebe4bc8220b14e 100644
    --- a/mm/mempolicy.c
    +++ b/mm/mempolicy.c
    @@ -3706,18 +3706,19 @@ static ssize_t weighted_interleave_auto_store(struct kobject *kobj,
     		new_wi_state->iw_table[i] = 1;
     
     	mutex_lock(&wi_state_lock);
    -	if (!input) {
    -		old_wi_state = rcu_dereference_protected(wi_state,
    -					lockdep_is_held(&wi_state_lock));
    -		if (!old_wi_state)
    -			goto update_wi_state;
    -		if (input == old_wi_state->mode_auto) {
    -			mutex_unlock(&wi_state_lock);
    -			return count;
    -		}
    +	old_wi_state = rcu_dereference_protected(wi_state,
    +				lockdep_is_held(&wi_state_lock));
     
    -		memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    -					       nr_node_ids * sizeof(u8));
    +	if (old_wi_state && input == old_wi_state->mode_auto) {
    +		mutex_unlock(&wi_state_lock);
    +		kfree(new_wi_state);
    +		return count;
    +	}
    +
    +	if (!input) {
    +		if (old_wi_state)
    +			memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    +						       nr_node_ids * sizeof(u8));
     		goto update_wi_state;
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
6fae274ce0e3

mm/mempolicy: fix memory leaks in weighted_interleave_auto_store()

1 file changed · +12 12
  • mm/mempolicy.c+12 12 modified
    diff --git a/mm/mempolicy.c b/mm/mempolicy.c
    index fd08771e2057b3..62108a5b74c4ed 100644
    --- a/mm/mempolicy.c
    +++ b/mm/mempolicy.c
    @@ -3700,18 +3700,19 @@ static ssize_t weighted_interleave_auto_store(struct kobject *kobj,
     		new_wi_state->iw_table[i] = 1;
     
     	mutex_lock(&wi_state_lock);
    -	if (!input) {
    -		old_wi_state = rcu_dereference_protected(wi_state,
    -					lockdep_is_held(&wi_state_lock));
    -		if (!old_wi_state)
    -			goto update_wi_state;
    -		if (input == old_wi_state->mode_auto) {
    -			mutex_unlock(&wi_state_lock);
    -			return count;
    -		}
    +	old_wi_state = rcu_dereference_protected(wi_state,
    +				lockdep_is_held(&wi_state_lock));
     
    -		memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    -					       nr_node_ids * sizeof(u8));
    +	if (old_wi_state && input == old_wi_state->mode_auto) {
    +		mutex_unlock(&wi_state_lock);
    +		kfree(new_wi_state);
    +		return count;
    +	}
    +
    +	if (!input) {
    +		if (old_wi_state)
    +			memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    +						       nr_node_ids * sizeof(u8));
     		goto update_wi_state;
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
c42a7efb9060

mm/mempolicy: fix memory leaks in weighted_interleave_auto_store()

1 file changed · +12 12
  • mm/mempolicy.c+12 12 modified
    diff --git a/mm/mempolicy.c b/mm/mempolicy.c
    index 94327574fbbbbf..779a8cc17a8324 100644
    --- a/mm/mempolicy.c
    +++ b/mm/mempolicy.c
    @@ -3636,18 +3636,19 @@ static ssize_t weighted_interleave_auto_store(struct kobject *kobj,
     		new_wi_state->iw_table[i] = 1;
     
     	mutex_lock(&wi_state_lock);
    -	if (!input) {
    -		old_wi_state = rcu_dereference_protected(wi_state,
    -					lockdep_is_held(&wi_state_lock));
    -		if (!old_wi_state)
    -			goto update_wi_state;
    -		if (input == old_wi_state->mode_auto) {
    -			mutex_unlock(&wi_state_lock);
    -			return count;
    -		}
    +	old_wi_state = rcu_dereference_protected(wi_state,
    +				lockdep_is_held(&wi_state_lock));
     
    -		memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    -					       nr_node_ids * sizeof(u8));
    +	if (old_wi_state && input == old_wi_state->mode_auto) {
    +		mutex_unlock(&wi_state_lock);
    +		kfree(new_wi_state);
    +		return count;
    +	}
    +
    +	if (!input) {
    +		if (old_wi_state)
    +			memcpy(new_wi_state->iw_table, old_wi_state->iw_table,
    +						       nr_node_ids * sizeof(u8));
     		goto update_wi_state;
     	}
     
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Conditional placement of old_wi_state fetch inside the `if (!input)` block causes two memory leak paths: early return without freeing new_wi_state when writing "false" in manual mode, and NULL old_wi_state when writing "true" so the old state is never freed after being overwritten."

Attack vector

An attacker with local access to the sysfs interface for weighted interleave auto-tuning can trigger memory leaks by writing specific values. Writing "false" when the current mode is already manual causes an early return that leaks the freshly allocated `new_wi_state`. Writing "true" causes `old_wi_state` to remain NULL because the fetch is inside the `!input` block, so the old state is overwritten by `rcu_assign_pointer()` but never freed. A user can repeatedly write "1" in a loop to exhaust kernel memory [patch_id=2660195].

Affected code

The vulnerability is in the `weighted_interleave_auto_store()` function in `mm/mempolicy.c` [patch_id=2660195]. The buggy code placed the `old_wi_state = rcu_dereference_protected(wi_state, ...)` fetch inside the `if (!input)` block, so it was only executed when the input was "false" (0).

What the fix does

The patch moves the `old_wi_state = rcu_dereference_protected(...)` fetch before the `if (!input)` check, making it unconditional [patch_id=2660195]. This ensures `old_wi_state` is always populated regardless of the input value. The early-return path for matching modes now also calls `kfree(new_wi_state)` before returning, preventing leak #1. When `!input` is true, the code conditionally copies the iw_table only if `old_wi_state` is non-NULL, and the old state will be properly freed via the existing cleanup path since `old_wi_state` is now always set.

Preconditions

  • authAttacker must have local access to the sysfs interface for weighted interleave auto-tuning (the `weighted_interleave_auto_store` function).
  • configThe kernel must be built with CONFIG_NUMA and the weighted interleave auto-tuning feature enabled (introduced in v6.16).
  • authNo special capabilities beyond write access to the sysfs file are required.

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.