VYPR
Unrated severityNVD Advisory· Published Jun 3, 2026

CVE-2026-46256

CVE-2026-46256

Description

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

NFS/localio: prevent direct reclaim recursion into NFS via nfs_writepages

LOCALIO is an NFS loopback mount optimization that avoids using the network for READ, WRITE and COMMIT if the NFS client and server are determined to be on the same system. But because LOCALIO is still fundamentally "just NFS loopback mount" it is susceptible to recursion deadlock via direct reclaim, e.g.: NFS LOCALIO down to XFS and then back into NFS via nfs_writepages.

Fix LOCALIO's potential for direct reclaim deadlock by ensuring that all its page cache allocations are done from GFP_NOFS context.

Thanks to Ben Coddington for pointing out commit ad22c7a043c2 ("xfs: prevent stack overflows from page cache allocation").

Affected products

2

Patches

6
67435d2d8a33

NFS/localio: prevent direct reclaim recursion into NFS via nfs_writepages

1 file changed · +15 1
  • fs/nfs/localio.c+15 1 modified
    diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
    index 7f5e7f0e3e00d..40e20b324b3fc 100644
    --- a/fs/nfs/localio.c
    +++ b/fs/nfs/localio.c
    @@ -291,6 +291,18 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
     }
     EXPORT_SYMBOL_GPL(nfs_local_open_fh);
     
    +/*
    + * Ensure all page cache allocations are done from GFP_NOFS context to
    + * prevent direct reclaim recursion back into NFS via nfs_writepages.
    + */
    +static void
    +nfs_local_mapping_set_gfp_nofs_context(struct address_space *m)
    +{
    +	gfp_t gfp_mask = mapping_gfp_mask(m);
    +
    +	mapping_set_gfp_mask(m, (gfp_mask & ~(__GFP_FS)));
    +}
    +
     static void
     nfs_local_iocb_free(struct nfs_local_kiocb *iocb)
     {
    @@ -315,6 +327,7 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
     		return NULL;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(file->f_mapping);
     	init_sync_kiocb(&iocb->kiocb, file);
     
     	iocb->hdr = hdr;
    @@ -1000,6 +1013,8 @@ nfs_local_run_commit(struct file *filp, struct nfs_commit_data *data)
     			end = LLONG_MAX;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(filp->f_mapping);
    +
     	dprintk("%s: commit %llu - %llu\n", __func__, start, end);
     	return vfs_fsync_range(filp, start, end, 0);
     }
    -- 
    cgit 1.3-korg
    
    
    
ae26a4cf2baf

NFS/localio: prevent direct reclaim recursion into NFS via nfs_writepages

1 file changed · +15 1
  • fs/nfs/localio.c+15 1 modified
    diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
    index ea7b35191d0af..ff430e6b773a5 100644
    --- a/fs/nfs/localio.c
    +++ b/fs/nfs/localio.c
    @@ -291,6 +291,18 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
     }
     EXPORT_SYMBOL_GPL(nfs_local_open_fh);
     
    +/*
    + * Ensure all page cache allocations are done from GFP_NOFS context to
    + * prevent direct reclaim recursion back into NFS via nfs_writepages.
    + */
    +static void
    +nfs_local_mapping_set_gfp_nofs_context(struct address_space *m)
    +{
    +	gfp_t gfp_mask = mapping_gfp_mask(m);
    +
    +	mapping_set_gfp_mask(m, (gfp_mask & ~(__GFP_FS)));
    +}
    +
     static void
     nfs_local_iocb_free(struct nfs_local_kiocb *iocb)
     {
    @@ -315,6 +327,7 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
     		return NULL;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(file->f_mapping);
     	init_sync_kiocb(&iocb->kiocb, file);
     
     	iocb->hdr = hdr;
    @@ -1010,6 +1023,8 @@ nfs_local_run_commit(struct file *filp, struct nfs_commit_data *data)
     			end = LLONG_MAX;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(filp->f_mapping);
    +
     	dprintk("%s: commit %llu - %llu\n", __func__, start, end);
     	return vfs_fsync_range(filp, start, end, 0);
     }
    -- 
    cgit 1.3-korg
    
    
    
6a5de0c4fc0f

NFS/localio: prevent direct reclaim recursion into NFS via nfs_writepages

1 file changed · +15 1
  • fs/nfs/localio.c+15 1 modified
    diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
    index 00bbac6c9fe40..84f53f27a9089 100644
    --- a/fs/nfs/localio.c
    +++ b/fs/nfs/localio.c
    @@ -291,6 +291,18 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
     }
     EXPORT_SYMBOL_GPL(nfs_local_open_fh);
     
    +/*
    + * Ensure all page cache allocations are done from GFP_NOFS context to
    + * prevent direct reclaim recursion back into NFS via nfs_writepages.
    + */
    +static void
    +nfs_local_mapping_set_gfp_nofs_context(struct address_space *m)
    +{
    +	gfp_t gfp_mask = mapping_gfp_mask(m);
    +
    +	mapping_set_gfp_mask(m, (gfp_mask & ~(__GFP_FS)));
    +}
    +
     static void
     nfs_local_iocb_free(struct nfs_local_kiocb *iocb)
     {
    @@ -315,6 +327,7 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
     		return NULL;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(file->f_mapping);
     	init_sync_kiocb(&iocb->kiocb, file);
     
     	iocb->hdr = hdr;
    @@ -1004,6 +1017,8 @@ nfs_local_run_commit(struct file *filp, struct nfs_commit_data *data)
     			end = LLONG_MAX;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(filp->f_mapping);
    +
     	dprintk("%s: commit %llu - %llu\n", __func__, start, end);
     	return vfs_fsync_range(filp, start, end, 0);
     }
    -- 
    cgit 1.3-korg
    
    
    
67435d2d8a33

NFS/localio: prevent direct reclaim recursion into NFS via nfs_writepages

1 file changed · +15 1
  • fs/nfs/localio.c+15 1 modified
    diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
    index 7f5e7f0e3e00d..40e20b324b3fc 100644
    --- a/fs/nfs/localio.c
    +++ b/fs/nfs/localio.c
    @@ -291,6 +291,18 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
     }
     EXPORT_SYMBOL_GPL(nfs_local_open_fh);
     
    +/*
    + * Ensure all page cache allocations are done from GFP_NOFS context to
    + * prevent direct reclaim recursion back into NFS via nfs_writepages.
    + */
    +static void
    +nfs_local_mapping_set_gfp_nofs_context(struct address_space *m)
    +{
    +	gfp_t gfp_mask = mapping_gfp_mask(m);
    +
    +	mapping_set_gfp_mask(m, (gfp_mask & ~(__GFP_FS)));
    +}
    +
     static void
     nfs_local_iocb_free(struct nfs_local_kiocb *iocb)
     {
    @@ -315,6 +327,7 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
     		return NULL;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(file->f_mapping);
     	init_sync_kiocb(&iocb->kiocb, file);
     
     	iocb->hdr = hdr;
    @@ -1000,6 +1013,8 @@ nfs_local_run_commit(struct file *filp, struct nfs_commit_data *data)
     			end = LLONG_MAX;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(filp->f_mapping);
    +
     	dprintk("%s: commit %llu - %llu\n", __func__, start, end);
     	return vfs_fsync_range(filp, start, end, 0);
     }
    -- 
    cgit 1.3-korg
    
    
    
6a5de0c4fc0f

NFS/localio: prevent direct reclaim recursion into NFS via nfs_writepages

1 file changed · +15 1
  • fs/nfs/localio.c+15 1 modified
    diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
    index 00bbac6c9fe40..84f53f27a9089 100644
    --- a/fs/nfs/localio.c
    +++ b/fs/nfs/localio.c
    @@ -291,6 +291,18 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
     }
     EXPORT_SYMBOL_GPL(nfs_local_open_fh);
     
    +/*
    + * Ensure all page cache allocations are done from GFP_NOFS context to
    + * prevent direct reclaim recursion back into NFS via nfs_writepages.
    + */
    +static void
    +nfs_local_mapping_set_gfp_nofs_context(struct address_space *m)
    +{
    +	gfp_t gfp_mask = mapping_gfp_mask(m);
    +
    +	mapping_set_gfp_mask(m, (gfp_mask & ~(__GFP_FS)));
    +}
    +
     static void
     nfs_local_iocb_free(struct nfs_local_kiocb *iocb)
     {
    @@ -315,6 +327,7 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
     		return NULL;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(file->f_mapping);
     	init_sync_kiocb(&iocb->kiocb, file);
     
     	iocb->hdr = hdr;
    @@ -1004,6 +1017,8 @@ nfs_local_run_commit(struct file *filp, struct nfs_commit_data *data)
     			end = LLONG_MAX;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(filp->f_mapping);
    +
     	dprintk("%s: commit %llu - %llu\n", __func__, start, end);
     	return vfs_fsync_range(filp, start, end, 0);
     }
    -- 
    cgit 1.3-korg
    
    
    
ae26a4cf2baf

NFS/localio: prevent direct reclaim recursion into NFS via nfs_writepages

1 file changed · +15 1
  • fs/nfs/localio.c+15 1 modified
    diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
    index ea7b35191d0af..ff430e6b773a5 100644
    --- a/fs/nfs/localio.c
    +++ b/fs/nfs/localio.c
    @@ -291,6 +291,18 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
     }
     EXPORT_SYMBOL_GPL(nfs_local_open_fh);
     
    +/*
    + * Ensure all page cache allocations are done from GFP_NOFS context to
    + * prevent direct reclaim recursion back into NFS via nfs_writepages.
    + */
    +static void
    +nfs_local_mapping_set_gfp_nofs_context(struct address_space *m)
    +{
    +	gfp_t gfp_mask = mapping_gfp_mask(m);
    +
    +	mapping_set_gfp_mask(m, (gfp_mask & ~(__GFP_FS)));
    +}
    +
     static void
     nfs_local_iocb_free(struct nfs_local_kiocb *iocb)
     {
    @@ -315,6 +327,7 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
     		return NULL;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(file->f_mapping);
     	init_sync_kiocb(&iocb->kiocb, file);
     
     	iocb->hdr = hdr;
    @@ -1010,6 +1023,8 @@ nfs_local_run_commit(struct file *filp, struct nfs_commit_data *data)
     			end = LLONG_MAX;
     	}
     
    +	nfs_local_mapping_set_gfp_nofs_context(filp->f_mapping);
    +
     	dprintk("%s: commit %llu - %llu\n", __func__, start, end);
     	return vfs_fsync_range(filp, start, end, 0);
     }
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"LOCALIO's page cache allocations could recursively call back into NFS, causing a deadlock."

Attack vector

An attacker could trigger a deadlock by exploiting the LOCALIO optimization in NFS. This occurs when NFS LOCALIO operations, intended to bypass network access for local operations, recursively call back into NFS through mechanisms like nfs_writepages. This recursion can happen when interacting with file systems like XFS, leading to a deadlock situation.

Affected code

The vulnerability resides in the NFS LOCALIO implementation, specifically within the `fs/nfs/localio.c` file. The functions `nfs_local_iocb_alloc` and `nfs_local_run_commit` are modified to ensure that page cache allocations use the GFP_NOFS context. This change aims to prevent recursion into NFS via nfs_writepages.

What the fix does

The patch modifies the `fs/nfs/localio.c` file to ensure that all page cache allocations within LOCALIO are performed using the GFP_NOFS context. This change prevents the page cache allocations from triggering direct reclaim operations that could recursively re-enter NFS, thereby resolving the potential for a deadlock. The function `nfs_local_mapping_set_gfp_nofs_context` was introduced and called in `nfs_local_iocb_alloc` and `nfs_local_run_commit` to enforce this behavior [patch_id=4686661].

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

References

3

News mentions

2