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

CVE-2026-45944

CVE-2026-45944

Description

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

iommu/vt-d: Clear Present bit before tearing down context entry

When tearing down a context entry, the current implementation zeros the entire 128-bit entry using multiple 64-bit writes. This creates a window where the hardware can fetch a "torn" entry — where some fields are already zeroed while the 'Present' bit is still set — leading to unpredictable behavior or spurious faults.

While x86 provides strong write ordering, the compiler may reorder writes to the two 64-bit halves of the context entry. Even without compiler reordering, the hardware fetch is not guaranteed to be atomic with respect to multiple CPU writes.

Align with the "Guidance to Software for Invalidations" in the VT-d spec (Section 6.5.3.3) by implementing the recommended ownership handshake:

1. Clear only the 'Present' (P) bit of the context entry first to signal the transition of ownership from hardware to software. 2. Use dma_wmb() to ensure the cleared bit is visible to the IOMMU. 3. Perform the required cache and context-cache invalidation to ensure hardware no longer has cached references to the entry. 4. Fully zero out the entry only after the invalidation is complete.

Also, add a dma_wmb() to context_set_present() to ensure the entry is fully initialized before the 'Present' bit becomes visible.

Affected products

1

Patches

6
c1e4f1dccbe9

iommu/vt-d: Clear Present bit before tearing down context entry

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitLu BaoluJan 22, 2026Fixed in 7.0via kernel-cna
3 files changed · +27 4
  • drivers/iommu/intel/iommu.c+3 1 modified
    diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
    index 134302fbcd9263..c66cc51f9e51ee 100644
    --- a/drivers/iommu/intel/iommu.c
    +++ b/drivers/iommu/intel/iommu.c
    @@ -1240,10 +1240,12 @@ static void domain_context_clear_one(struct device_domain_info *info, u8 bus, u8
     	}
     
     	did = context_domain_id(context);
    -	context_clear_entry(context);
    +	context_clear_present(context);
     	__iommu_flush_cache(iommu, context, sizeof(*context));
     	spin_unlock(&iommu->lock);
     	intel_context_flush_no_pasid(info, context, did);
    +	context_clear_entry(context);
    +	__iommu_flush_cache(iommu, context, sizeof(*context));
     }
     
     int __domain_setup_first_level(struct intel_iommu *iommu, struct device *dev,
    
  • drivers/iommu/intel/iommu.h+20 1 modified
    diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
    index 25c5e22096d44e..599913fb65d59e 100644
    --- a/drivers/iommu/intel/iommu.h
    +++ b/drivers/iommu/intel/iommu.h
    @@ -900,7 +900,26 @@ static inline int pfn_level_offset(u64 pfn, int level)
     
     static inline void context_set_present(struct context_entry *context)
     {
    -	context->lo |= 1;
    +	u64 val;
    +
    +	dma_wmb();
    +	val = READ_ONCE(context->lo) | 1;
    +	WRITE_ONCE(context->lo, val);
    +}
    +
    +/*
    + * Clear the Present (P) bit (bit 0) of a context table entry. This initiates
    + * the transition of the entry's ownership from hardware to software. The
    + * caller is responsible for fulfilling the invalidation handshake recommended
    + * by the VT-d spec, Section 6.5.3.3 (Guidance to Software for Invalidations).
    + */
    +static inline void context_clear_present(struct context_entry *context)
    +{
    +	u64 val;
    +
    +	val = READ_ONCE(context->lo) & GENMASK_ULL(63, 1);
    +	WRITE_ONCE(context->lo, val);
    +	dma_wmb();
     }
     
     static inline void context_set_fault_enable(struct context_entry *context)
    
  • drivers/iommu/intel/pasid.c+4 2 modified
    diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c
    index 07e056b2460502..f5dfa9b9eb3e24 100644
    --- a/drivers/iommu/intel/pasid.c
    +++ b/drivers/iommu/intel/pasid.c
    @@ -1024,7 +1024,7 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     	}
     
     	if (context_copied(iommu, bus, devfn)) {
    -		context_clear_entry(context);
    +		context_clear_present(context);
     		__iommu_flush_cache(iommu, context, sizeof(*context));
     
     		/*
    @@ -1044,6 +1044,9 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     		iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
     		devtlb_invalidation_with_pasid(iommu, dev, IOMMU_NO_PASID);
     
    +		context_clear_entry(context);
    +		__iommu_flush_cache(iommu, context, sizeof(*context));
    +
     		/*
     		 * At this point, the device is supposed to finish reset at
     		 * its driver probe stage, so no in-flight DMA will exist,
    -- 
    cgit 1.3-korg
    
    
    
d2138abc8f0a

iommu/vt-d: Clear Present bit before tearing down context entry

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitLu BaoluJan 22, 2026Fixed in 6.18.14via kernel-cna
3 files changed · +27 4
  • drivers/iommu/intel/iommu.c+3 1 modified
    diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
    index e236c7ec221f4b..49e83c8566a32f 100644
    --- a/drivers/iommu/intel/iommu.c
    +++ b/drivers/iommu/intel/iommu.c
    @@ -1722,10 +1722,12 @@ static void domain_context_clear_one(struct device_domain_info *info, u8 bus, u8
     	}
     
     	did = context_domain_id(context);
    -	context_clear_entry(context);
    +	context_clear_present(context);
     	__iommu_flush_cache(iommu, context, sizeof(*context));
     	spin_unlock(&iommu->lock);
     	intel_context_flush_no_pasid(info, context, did);
    +	context_clear_entry(context);
    +	__iommu_flush_cache(iommu, context, sizeof(*context));
     }
     
     int __domain_setup_first_level(struct intel_iommu *iommu, struct device *dev,
    
  • drivers/iommu/intel/iommu.h+20 1 modified
    diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
    index dcc5466d35f933..9198ac7f6bbab2 100644
    --- a/drivers/iommu/intel/iommu.h
    +++ b/drivers/iommu/intel/iommu.h
    @@ -969,7 +969,26 @@ static inline unsigned long lvl_to_nr_pages(unsigned int lvl)
     
     static inline void context_set_present(struct context_entry *context)
     {
    -	context->lo |= 1;
    +	u64 val;
    +
    +	dma_wmb();
    +	val = READ_ONCE(context->lo) | 1;
    +	WRITE_ONCE(context->lo, val);
    +}
    +
    +/*
    + * Clear the Present (P) bit (bit 0) of a context table entry. This initiates
    + * the transition of the entry's ownership from hardware to software. The
    + * caller is responsible for fulfilling the invalidation handshake recommended
    + * by the VT-d spec, Section 6.5.3.3 (Guidance to Software for Invalidations).
    + */
    +static inline void context_clear_present(struct context_entry *context)
    +{
    +	u64 val;
    +
    +	val = READ_ONCE(context->lo) & GENMASK_ULL(63, 1);
    +	WRITE_ONCE(context->lo, val);
    +	dma_wmb();
     }
     
     static inline void context_set_fault_enable(struct context_entry *context)
    
  • drivers/iommu/intel/pasid.c+4 2 modified
    diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c
    index f64b5ae306d0f2..d13099a6cb9c3e 100644
    --- a/drivers/iommu/intel/pasid.c
    +++ b/drivers/iommu/intel/pasid.c
    @@ -1028,7 +1028,7 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     	}
     
     	if (context_copied(iommu, bus, devfn)) {
    -		context_clear_entry(context);
    +		context_clear_present(context);
     		__iommu_flush_cache(iommu, context, sizeof(*context));
     
     		/*
    @@ -1048,6 +1048,9 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     		iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
     		devtlb_invalidation_with_pasid(iommu, dev, IOMMU_NO_PASID);
     
    +		context_clear_entry(context);
    +		__iommu_flush_cache(iommu, context, sizeof(*context));
    +
     		/*
     		 * At this point, the device is supposed to finish reset at
     		 * its driver probe stage, so no in-flight DMA will exist,
    -- 
    cgit 1.3-korg
    
    
    
a922dbafb4a6

iommu/vt-d: Clear Present bit before tearing down context entry

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitLu BaoluJan 22, 2026Fixed in 6.19.4via kernel-cna
3 files changed · +27 4
  • drivers/iommu/intel/iommu.c+3 1 modified
    diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
    index 134302fbcd9263..c66cc51f9e51ee 100644
    --- a/drivers/iommu/intel/iommu.c
    +++ b/drivers/iommu/intel/iommu.c
    @@ -1240,10 +1240,12 @@ static void domain_context_clear_one(struct device_domain_info *info, u8 bus, u8
     	}
     
     	did = context_domain_id(context);
    -	context_clear_entry(context);
    +	context_clear_present(context);
     	__iommu_flush_cache(iommu, context, sizeof(*context));
     	spin_unlock(&iommu->lock);
     	intel_context_flush_no_pasid(info, context, did);
    +	context_clear_entry(context);
    +	__iommu_flush_cache(iommu, context, sizeof(*context));
     }
     
     int __domain_setup_first_level(struct intel_iommu *iommu, struct device *dev,
    
  • drivers/iommu/intel/iommu.h+20 1 modified
    diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
    index 25c5e22096d44e..599913fb65d59e 100644
    --- a/drivers/iommu/intel/iommu.h
    +++ b/drivers/iommu/intel/iommu.h
    @@ -900,7 +900,26 @@ static inline int pfn_level_offset(u64 pfn, int level)
     
     static inline void context_set_present(struct context_entry *context)
     {
    -	context->lo |= 1;
    +	u64 val;
    +
    +	dma_wmb();
    +	val = READ_ONCE(context->lo) | 1;
    +	WRITE_ONCE(context->lo, val);
    +}
    +
    +/*
    + * Clear the Present (P) bit (bit 0) of a context table entry. This initiates
    + * the transition of the entry's ownership from hardware to software. The
    + * caller is responsible for fulfilling the invalidation handshake recommended
    + * by the VT-d spec, Section 6.5.3.3 (Guidance to Software for Invalidations).
    + */
    +static inline void context_clear_present(struct context_entry *context)
    +{
    +	u64 val;
    +
    +	val = READ_ONCE(context->lo) & GENMASK_ULL(63, 1);
    +	WRITE_ONCE(context->lo, val);
    +	dma_wmb();
     }
     
     static inline void context_set_fault_enable(struct context_entry *context)
    
  • drivers/iommu/intel/pasid.c+4 2 modified
    diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c
    index b611ad070e729c..db535385778bd2 100644
    --- a/drivers/iommu/intel/pasid.c
    +++ b/drivers/iommu/intel/pasid.c
    @@ -1024,7 +1024,7 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     	}
     
     	if (context_copied(iommu, bus, devfn)) {
    -		context_clear_entry(context);
    +		context_clear_present(context);
     		__iommu_flush_cache(iommu, context, sizeof(*context));
     
     		/*
    @@ -1044,6 +1044,9 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     		iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
     		devtlb_invalidation_with_pasid(iommu, dev, IOMMU_NO_PASID);
     
    +		context_clear_entry(context);
    +		__iommu_flush_cache(iommu, context, sizeof(*context));
    +
     		/*
     		 * At this point, the device is supposed to finish reset at
     		 * its driver probe stage, so no in-flight DMA will exist,
    -- 
    cgit 1.3-korg
    
    
    
a922dbafb4a6

iommu/vt-d: Clear Present bit before tearing down context entry

3 files changed · +27 4
  • drivers/iommu/intel/iommu.c+3 1 modified
    diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
    index 134302fbcd9263..c66cc51f9e51ee 100644
    --- a/drivers/iommu/intel/iommu.c
    +++ b/drivers/iommu/intel/iommu.c
    @@ -1240,10 +1240,12 @@ static void domain_context_clear_one(struct device_domain_info *info, u8 bus, u8
     	}
     
     	did = context_domain_id(context);
    -	context_clear_entry(context);
    +	context_clear_present(context);
     	__iommu_flush_cache(iommu, context, sizeof(*context));
     	spin_unlock(&iommu->lock);
     	intel_context_flush_no_pasid(info, context, did);
    +	context_clear_entry(context);
    +	__iommu_flush_cache(iommu, context, sizeof(*context));
     }
     
     int __domain_setup_first_level(struct intel_iommu *iommu, struct device *dev,
    
  • drivers/iommu/intel/iommu.h+20 1 modified
    diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
    index 25c5e22096d44e..599913fb65d59e 100644
    --- a/drivers/iommu/intel/iommu.h
    +++ b/drivers/iommu/intel/iommu.h
    @@ -900,7 +900,26 @@ static inline int pfn_level_offset(u64 pfn, int level)
     
     static inline void context_set_present(struct context_entry *context)
     {
    -	context->lo |= 1;
    +	u64 val;
    +
    +	dma_wmb();
    +	val = READ_ONCE(context->lo) | 1;
    +	WRITE_ONCE(context->lo, val);
    +}
    +
    +/*
    + * Clear the Present (P) bit (bit 0) of a context table entry. This initiates
    + * the transition of the entry's ownership from hardware to software. The
    + * caller is responsible for fulfilling the invalidation handshake recommended
    + * by the VT-d spec, Section 6.5.3.3 (Guidance to Software for Invalidations).
    + */
    +static inline void context_clear_present(struct context_entry *context)
    +{
    +	u64 val;
    +
    +	val = READ_ONCE(context->lo) & GENMASK_ULL(63, 1);
    +	WRITE_ONCE(context->lo, val);
    +	dma_wmb();
     }
     
     static inline void context_set_fault_enable(struct context_entry *context)
    
  • drivers/iommu/intel/pasid.c+4 2 modified
    diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c
    index b611ad070e729c..db535385778bd2 100644
    --- a/drivers/iommu/intel/pasid.c
    +++ b/drivers/iommu/intel/pasid.c
    @@ -1024,7 +1024,7 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     	}
     
     	if (context_copied(iommu, bus, devfn)) {
    -		context_clear_entry(context);
    +		context_clear_present(context);
     		__iommu_flush_cache(iommu, context, sizeof(*context));
     
     		/*
    @@ -1044,6 +1044,9 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     		iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
     		devtlb_invalidation_with_pasid(iommu, dev, IOMMU_NO_PASID);
     
    +		context_clear_entry(context);
    +		__iommu_flush_cache(iommu, context, sizeof(*context));
    +
     		/*
     		 * At this point, the device is supposed to finish reset at
     		 * its driver probe stage, so no in-flight DMA will exist,
    -- 
    cgit 1.3-korg
    
    
    
c1e4f1dccbe9

iommu/vt-d: Clear Present bit before tearing down context entry

3 files changed · +27 4
  • drivers/iommu/intel/iommu.c+3 1 modified
    diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
    index 134302fbcd9263..c66cc51f9e51ee 100644
    --- a/drivers/iommu/intel/iommu.c
    +++ b/drivers/iommu/intel/iommu.c
    @@ -1240,10 +1240,12 @@ static void domain_context_clear_one(struct device_domain_info *info, u8 bus, u8
     	}
     
     	did = context_domain_id(context);
    -	context_clear_entry(context);
    +	context_clear_present(context);
     	__iommu_flush_cache(iommu, context, sizeof(*context));
     	spin_unlock(&iommu->lock);
     	intel_context_flush_no_pasid(info, context, did);
    +	context_clear_entry(context);
    +	__iommu_flush_cache(iommu, context, sizeof(*context));
     }
     
     int __domain_setup_first_level(struct intel_iommu *iommu, struct device *dev,
    
  • drivers/iommu/intel/iommu.h+20 1 modified
    diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
    index 25c5e22096d44e..599913fb65d59e 100644
    --- a/drivers/iommu/intel/iommu.h
    +++ b/drivers/iommu/intel/iommu.h
    @@ -900,7 +900,26 @@ static inline int pfn_level_offset(u64 pfn, int level)
     
     static inline void context_set_present(struct context_entry *context)
     {
    -	context->lo |= 1;
    +	u64 val;
    +
    +	dma_wmb();
    +	val = READ_ONCE(context->lo) | 1;
    +	WRITE_ONCE(context->lo, val);
    +}
    +
    +/*
    + * Clear the Present (P) bit (bit 0) of a context table entry. This initiates
    + * the transition of the entry's ownership from hardware to software. The
    + * caller is responsible for fulfilling the invalidation handshake recommended
    + * by the VT-d spec, Section 6.5.3.3 (Guidance to Software for Invalidations).
    + */
    +static inline void context_clear_present(struct context_entry *context)
    +{
    +	u64 val;
    +
    +	val = READ_ONCE(context->lo) & GENMASK_ULL(63, 1);
    +	WRITE_ONCE(context->lo, val);
    +	dma_wmb();
     }
     
     static inline void context_set_fault_enable(struct context_entry *context)
    
  • drivers/iommu/intel/pasid.c+4 2 modified
    diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c
    index 07e056b2460502..f5dfa9b9eb3e24 100644
    --- a/drivers/iommu/intel/pasid.c
    +++ b/drivers/iommu/intel/pasid.c
    @@ -1024,7 +1024,7 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     	}
     
     	if (context_copied(iommu, bus, devfn)) {
    -		context_clear_entry(context);
    +		context_clear_present(context);
     		__iommu_flush_cache(iommu, context, sizeof(*context));
     
     		/*
    @@ -1044,6 +1044,9 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     		iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
     		devtlb_invalidation_with_pasid(iommu, dev, IOMMU_NO_PASID);
     
    +		context_clear_entry(context);
    +		__iommu_flush_cache(iommu, context, sizeof(*context));
    +
     		/*
     		 * At this point, the device is supposed to finish reset at
     		 * its driver probe stage, so no in-flight DMA will exist,
    -- 
    cgit 1.3-korg
    
    
    
d2138abc8f0a

iommu/vt-d: Clear Present bit before tearing down context entry

3 files changed · +27 4
  • drivers/iommu/intel/iommu.c+3 1 modified
    diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
    index e236c7ec221f4b..49e83c8566a32f 100644
    --- a/drivers/iommu/intel/iommu.c
    +++ b/drivers/iommu/intel/iommu.c
    @@ -1722,10 +1722,12 @@ static void domain_context_clear_one(struct device_domain_info *info, u8 bus, u8
     	}
     
     	did = context_domain_id(context);
    -	context_clear_entry(context);
    +	context_clear_present(context);
     	__iommu_flush_cache(iommu, context, sizeof(*context));
     	spin_unlock(&iommu->lock);
     	intel_context_flush_no_pasid(info, context, did);
    +	context_clear_entry(context);
    +	__iommu_flush_cache(iommu, context, sizeof(*context));
     }
     
     int __domain_setup_first_level(struct intel_iommu *iommu, struct device *dev,
    
  • drivers/iommu/intel/iommu.h+20 1 modified
    diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
    index dcc5466d35f933..9198ac7f6bbab2 100644
    --- a/drivers/iommu/intel/iommu.h
    +++ b/drivers/iommu/intel/iommu.h
    @@ -969,7 +969,26 @@ static inline unsigned long lvl_to_nr_pages(unsigned int lvl)
     
     static inline void context_set_present(struct context_entry *context)
     {
    -	context->lo |= 1;
    +	u64 val;
    +
    +	dma_wmb();
    +	val = READ_ONCE(context->lo) | 1;
    +	WRITE_ONCE(context->lo, val);
    +}
    +
    +/*
    + * Clear the Present (P) bit (bit 0) of a context table entry. This initiates
    + * the transition of the entry's ownership from hardware to software. The
    + * caller is responsible for fulfilling the invalidation handshake recommended
    + * by the VT-d spec, Section 6.5.3.3 (Guidance to Software for Invalidations).
    + */
    +static inline void context_clear_present(struct context_entry *context)
    +{
    +	u64 val;
    +
    +	val = READ_ONCE(context->lo) & GENMASK_ULL(63, 1);
    +	WRITE_ONCE(context->lo, val);
    +	dma_wmb();
     }
     
     static inline void context_set_fault_enable(struct context_entry *context)
    
  • drivers/iommu/intel/pasid.c+4 2 modified
    diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c
    index f64b5ae306d0f2..d13099a6cb9c3e 100644
    --- a/drivers/iommu/intel/pasid.c
    +++ b/drivers/iommu/intel/pasid.c
    @@ -1028,7 +1028,7 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     	}
     
     	if (context_copied(iommu, bus, devfn)) {
    -		context_clear_entry(context);
    +		context_clear_present(context);
     		__iommu_flush_cache(iommu, context, sizeof(*context));
     
     		/*
    @@ -1048,6 +1048,9 @@ static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
     		iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
     		devtlb_invalidation_with_pasid(iommu, dev, IOMMU_NO_PASID);
     
    +		context_clear_entry(context);
    +		__iommu_flush_cache(iommu, context, sizeof(*context));
    +
     		/*
     		 * At this point, the device is supposed to finish reset at
     		 * its driver probe stage, so no in-flight DMA will exist,
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing ownership handshake when tearing down IOMMU context entries — the Present bit was not cleared before zeroing the entry, creating a race window where hardware could fetch a torn entry."

Attack vector

An attacker with DMA-capable device access could trigger the race window during IOMMU context-entry teardown. The original code zeroed the 128-bit context entry with two separate 64-bit writes; if the IOMMU hardware fetched the entry after the first write but before the second, it would see a "torn" entry where some fields are zeroed but the Present (P) bit is still set [patch_id=2661123]. This can cause the IOMMU to use partially-invalid translation data, leading to unpredictable DMA behavior or spurious faults. The attack requires the ability to trigger device removal, driver unbind, or domain reconfiguration that invokes the context-clearing path.

Affected code

The vulnerability resides in the Intel VT-d IOMMU driver, specifically in the context-entry teardown path. The affected functions are `domain_context_clear_one()` in `drivers/iommu/intel/iommu.c` and `device_pasid_table_setup()` in `drivers/iommu/intel/pasid.c`, both of which previously called `context_clear_entry()` to zero the entire 128-bit context entry without first clearing the Present bit [patch_id=2661123]. The helper `context_set_present()` in `drivers/iommu/intel/iommu.h` also lacked a write memory barrier before setting the Present bit.

What the fix does

The patch implements the ownership handshake recommended by the VT-d spec (Section 6.5.3.3). A new helper `context_clear_present()` clears only bit 0 (the Present bit) of the context entry's low 64-bit half using a single `WRITE_ONCE`, followed by a `dma_wmb()` to ensure visibility to the IOMMU [patch_id=2661123]. The call sites in `domain_context_clear_one()` and `device_pasid_table_setup()` now first clear Present, flush the cache, perform the required IOTLB/context-cache invalidation, and only then call `context_clear_entry()` to zero the full entry. Additionally, `context_set_present()` now uses `READ_ONCE`/`WRITE_ONCE` with a preceding `dma_wmb()` to guarantee all other fields are initialized before the Present bit becomes visible to hardware.

Preconditions

  • inputAttacker must have control over a DMA-capable device whose IOMMU context entry is being torn down
  • configThe teardown path (device removal, driver unbind, or domain reconfiguration) must be triggered while DMA transactions may still be in-flight

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.