Re: [edk2] [PATCH 2/3] UefiCpuPkg/CpuDxe: fix an infinite loop issue

2018-10-22 Thread Wang, Jian J
Laszlo,

Thanks for the comments.

Regards,
Jian


> -Original Message-
> From: Laszlo Ersek [mailto:ler...@redhat.com]
> Sent: Friday, October 19, 2018 7:46 PM
> To: Wang, Jian J ; edk2-devel@lists.01.org
> Cc: Zeng, Star ; Kinney, Michael D
> ; Yao, Jiewen ; Ni, Ruiyu
> 
> Subject: Re: [PATCH 2/3] UefiCpuPkg/CpuDxe: fix an infinite loop issue
> 
> On 10/19/18 03:50, Jian J Wang wrote:
> > The UAF (Use-After-Free) memory detection feature will cause an
> > infinite calling of InitializePageTablePool(). This is due to a
> > fact that AllocateAlignedPages() is used to allocate page table
> > pool memory. This function will most likely call gBS->FreePages
> > to free unaligned pages and then cause another round of page
> > attributes change, like below
> >
> >FreePages() <===|
> > => SetMemoryAttributes()   |
> 
> This should likely be "SetMemorySpaceAttributes" (the DXE service), or else
> "CpuSetMemoryAttributes" (the underlying CpuDxe function name).
> 

You're right. I'll change it.

> > =>  |
> > => InitializePageTablePool()   |
> > => AllocateAlignedPages()  |
> > => FreePages() |
> >
> > The solution is add a lock in page table pool allocation function
> > and fail any other requests if it has not been done.
> 
> OK, but what is the end result? InitializePageTablePool() will return FALSE. 
> How
> far back up is that error propagated? To what components will the error be
> visible?
> 
> BTW, I've found the following comment in CpuSetMemoryAttributes():
> 
>   //
>   // During memory attributes updating, new pages may be allocated to setup
>   // smaller granularity of page table. Page allocation action might then 
> cause
>   // another calling of CpuSetMemoryAttributes() recursively, due to memory
>   // protection policy configured (such as PcdDxeNxMemoryProtectionPolicy).
>   // Since this driver will always protect memory used as page table by 
> itself,
>   // there's no need to apply protection policy requested from memory service.
>   // So it's safe to just return EFI_SUCCESS if this time of calling is caused
>   // by page table memory allocation.
>   //
> 
> Is the current argument similar? I think it should be documented somehow.
> 

No, I don't think they're the similar. The issue I encountered here is that the 
code
tries to set freed memory as not-present but trapped in dead loop. The only
consequence here is that the freed pages in AllocateAlignedPages() cannot be
set as not-present. But it's ok because they're just allocated and haven't been
used by any other code.

> >
> > Cc: Laszlo Ersek 
> > Cc: Star Zeng 
> > Cc: Michael D Kinney 
> > Cc: Jiewen Yao 
> > Cc: Ruiyu Ni 
> > Contributed-under: TianoCore Contribution Agreement 1.1
> > Signed-off-by: Jian J Wang 
> > ---
> >  UefiCpuPkg/CpuDxe/CpuPageTable.c | 12 
> >  1 file changed, 12 insertions(+)
> >
> > diff --git a/UefiCpuPkg/CpuDxe/CpuPageTable.c
> b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> > index 33e8ee2d2c..2145e623fa 100644
> > --- a/UefiCpuPkg/CpuDxe/CpuPageTable.c
> > +++ b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> > @@ -100,6 +100,7 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
> >  };
> >
> >  PAGE_TABLE_POOL   *mPageTablePool = NULL;
> > +EFI_LOCK  mPageTablePoolLock =
> EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
> 
> Why does this have to be an "EFI_LOCK"? Can't we just use a global variable? 
> (I
> don't understand why messing with the TPL is necessary.)
> 
> In fact, I totally don't understand the point of EfiAcquireLock(). If we have 
> two
> independent resources, each protected with its own separate lock, then why do
> both locks share the system-wide TPL between each other?
> 

Maybe you're right. Lock is a bit overkill. I'll try a global to find out if 
it's ok.

> 
> >  PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext;
> >  EFI_SMM_BASE2_PROTOCOL*mSmmBase2 = NULL;
> >
> > @@ -1045,6 +1046,12 @@ InitializePageTablePool (
> >  {
> >VOID  *Buffer;
> >BOOLEAN   IsModified;
> > +  EFI_STATUSStatus;
> > +
> > +  Status = EfiAcquireLockOrFail ();
> > +  if (EFI_ERROR (Status)) {
> > +return FALSE;
> > +  }
> >
> >//
> >// Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one
> page for
> > @@ -1056,7 +1063,10 @@ InitializePageTablePool (
> >Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
> >if (Buffer == NULL) {
> >  DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
> > +EfiReleaseLock ();
> 
> I feel that it would be safer to introduce a "Done" label at the bottom of the
> function, and release the lock there.
> 
> (Again, I'm not sure why this has to be a "lock".)
> 

Agree. I'll update this part of logic.

> >  return FALSE;
> > +  } else {
> > +DEBUG ((DEBUG_INFO, "Paging: added %d pages to page table pool\r\n",
> PoolPages));
> 
> Please don't print UINTN values 

Re: [edk2] [PATCH 2/3] UefiCpuPkg/CpuDxe: fix an infinite loop issue

2018-10-19 Thread Laszlo Ersek
On 10/19/18 03:50, Jian J Wang wrote:
> The UAF (Use-After-Free) memory detection feature will cause an
> infinite calling of InitializePageTablePool(). This is due to a
> fact that AllocateAlignedPages() is used to allocate page table
> pool memory. This function will most likely call gBS->FreePages
> to free unaligned pages and then cause another round of page
> attributes change, like below
> 
>FreePages() <===|
> => SetMemoryAttributes()   |

This should likely be "SetMemorySpaceAttributes" (the DXE service), or else 
"CpuSetMemoryAttributes" (the underlying CpuDxe function name).

> =>  |
> => InitializePageTablePool()   |
> => AllocateAlignedPages()  |
> => FreePages() |
> 
> The solution is add a lock in page table pool allocation function
> and fail any other requests if it has not been done.

OK, but what is the end result? InitializePageTablePool() will return FALSE. 
How far back up is that error propagated? To what components will the error be 
visible?

BTW, I've found the following comment in CpuSetMemoryAttributes():

  //
  // During memory attributes updating, new pages may be allocated to setup
  // smaller granularity of page table. Page allocation action might then cause
  // another calling of CpuSetMemoryAttributes() recursively, due to memory
  // protection policy configured (such as PcdDxeNxMemoryProtectionPolicy).
  // Since this driver will always protect memory used as page table by itself,
  // there's no need to apply protection policy requested from memory service.
  // So it's safe to just return EFI_SUCCESS if this time of calling is caused
  // by page table memory allocation.
  //

Is the current argument similar? I think it should be documented somehow.

> 
> Cc: Laszlo Ersek 
> Cc: Star Zeng 
> Cc: Michael D Kinney 
> Cc: Jiewen Yao 
> Cc: Ruiyu Ni 
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Jian J Wang 
> ---
>  UefiCpuPkg/CpuDxe/CpuPageTable.c | 12 
>  1 file changed, 12 insertions(+)
> 
> diff --git a/UefiCpuPkg/CpuDxe/CpuPageTable.c 
> b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> index 33e8ee2d2c..2145e623fa 100644
> --- a/UefiCpuPkg/CpuDxe/CpuPageTable.c
> +++ b/UefiCpuPkg/CpuDxe/CpuPageTable.c
> @@ -100,6 +100,7 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
>  };
>  
>  PAGE_TABLE_POOL   *mPageTablePool = NULL;
> +EFI_LOCK  mPageTablePoolLock = 
> EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);

Why does this have to be an "EFI_LOCK"? Can't we just use a global variable? (I 
don't understand why messing with the TPL is necessary.)

In fact, I totally don't understand the point of EfiAcquireLock(). If we have 
two independent resources, each protected with its own separate lock, then why 
do both locks share the system-wide TPL between each other?


>  PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext;
>  EFI_SMM_BASE2_PROTOCOL*mSmmBase2 = NULL;
>  
> @@ -1045,6 +1046,12 @@ InitializePageTablePool (
>  {
>VOID  *Buffer;
>BOOLEAN   IsModified;
> +  EFI_STATUSStatus;
> +
> +  Status = EfiAcquireLockOrFail ();
> +  if (EFI_ERROR (Status)) {
> +return FALSE;
> +  }
>  
>//
>// Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page 
> for
> @@ -1056,7 +1063,10 @@ InitializePageTablePool (
>Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
>if (Buffer == NULL) {
>  DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
> +EfiReleaseLock ();

I feel that it would be safer to introduce a "Done" label at the bottom of the 
function, and release the lock there.

(Again, I'm not sure why this has to be a "lock".)

>  return FALSE;
> +  } else {
> +DEBUG ((DEBUG_INFO, "Paging: added %d pages to page table pool\r\n", 
> PoolPages));

Please don't print UINTN values with %d. Cast them to UINT64 and log them with 
%lu.

>}
>  
>//
> @@ -1092,6 +1102,8 @@ InitializePageTablePool (
>  );
>ASSERT (IsModified == TRUE);
>  
> +  EfiReleaseLock ();
> +
>return TRUE;
>  }
>  
> 

Thanks
Laszlo
___
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel


[edk2] [PATCH 2/3] UefiCpuPkg/CpuDxe: fix an infinite loop issue

2018-10-18 Thread Jian J Wang
The UAF (Use-After-Free) memory detection feature will cause an
infinite calling of InitializePageTablePool(). This is due to a
fact that AllocateAlignedPages() is used to allocate page table
pool memory. This function will most likely call gBS->FreePages
to free unaligned pages and then cause another round of page
attributes change, like below

   FreePages() <===|
=> SetMemoryAttributes()   |
=>  |
=> InitializePageTablePool()   |
=> AllocateAlignedPages()  |
=> FreePages() |

The solution is add a lock in page table pool allocation function
and fail any other requests if it has not been done.

Cc: Laszlo Ersek 
Cc: Star Zeng 
Cc: Michael D Kinney 
Cc: Jiewen Yao 
Cc: Ruiyu Ni 
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jian J Wang 
---
 UefiCpuPkg/CpuDxe/CpuPageTable.c | 12 
 1 file changed, 12 insertions(+)

diff --git a/UefiCpuPkg/CpuDxe/CpuPageTable.c b/UefiCpuPkg/CpuDxe/CpuPageTable.c
index 33e8ee2d2c..2145e623fa 100644
--- a/UefiCpuPkg/CpuDxe/CpuPageTable.c
+++ b/UefiCpuPkg/CpuDxe/CpuPageTable.c
@@ -100,6 +100,7 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
 };
 
 PAGE_TABLE_POOL   *mPageTablePool = NULL;
+EFI_LOCK  mPageTablePoolLock = 
EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
 PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext;
 EFI_SMM_BASE2_PROTOCOL*mSmmBase2 = NULL;
 
@@ -1045,6 +1046,12 @@ InitializePageTablePool (
 {
   VOID  *Buffer;
   BOOLEAN   IsModified;
+  EFI_STATUSStatus;
+
+  Status = EfiAcquireLockOrFail ();
+  if (EFI_ERROR (Status)) {
+return FALSE;
+  }
 
   //
   // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
@@ -1056,7 +1063,10 @@ InitializePageTablePool (
   Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
   if (Buffer == NULL) {
 DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
+EfiReleaseLock ();
 return FALSE;
+  } else {
+DEBUG ((DEBUG_INFO, "Paging: added %d pages to page table pool\r\n", 
PoolPages));
   }
 
   //
@@ -1092,6 +1102,8 @@ InitializePageTablePool (
 );
   ASSERT (IsModified == TRUE);
 
+  EfiReleaseLock ();
+
   return TRUE;
 }
 
-- 
2.16.2.windows.1

___
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel