crypto/ccp: Fix locking on alloc failure handling
Subsystems (3)
PATCH 1/1 crypto/ccp: Fix locking on alloc failure handling
The __snp_alloc_firmware_pages() helper allocates pages in the firmware state (alloc + rmpupdate). In case of failed rmpupdate, it tries reclaiming pages with already changed state. This requires calling the PSP firmware and since there is sev_cmd_mutex to guard such calls, the helper takes a "locked" parameter so specify if the lock needs to be held.
Most calls happen from snp_alloc_firmware_page() which executes without the lock. However
commit 24512afa4336 ("crypto: ccp: Handle the legacy TMR allocation when SNP is enabled")
switched sev_fw_alloc() from alloc_pages() (which does not call the PSP) to __snp_alloc_firmware_pages() (which does) but did not account for the fact that sev_fw_alloc() is called from __sev_platform_init_locked() (via __sev_platform_init_handle_tmr()) and executes with the lock held.
Add a "locked" parameter to __snp_alloc_firmware_pages().
Make sev_fw_alloc() use the new parameter to prevent potential deadlock in rmp_mark_pages_firmware() if rmpupdate() failed.
--- drivers/crypto/ccp/sev-dev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/crypto/ccp/sev-dev.c +4 -4
@@ -434,7 +434,7 @@ static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, booreturn rc; } -static struct page *__snp_alloc_firmware_pages (gfp_t gfp_mask,int order) +static struct page *__snp_alloc_firmware_pages (gfp_t gfp_mask,int order,bool locked) {unsigned long npages =1ul << order, paddr;struct sev_device *sev;
@@ -453,7 +453,7 @@ static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order)return page; paddr =__pa ((unsigned long )page_address (page)); -if (rmp_mark_pages_firmware (paddr, npages, false)) +if (rmp_mark_pages_firmware (paddr, npages, locked))return NULL ;return page;
@@ -463,7 +463,7 @@ void *snp_alloc_firmware_page(gfp_t gfp_mask) {struct page *page; - page =__snp_alloc_firmware_pages (gfp_mask,0 ); + page =__snp_alloc_firmware_pages (gfp_mask,0 , false);return page ?page_address (page) :NULL ; }
@@ -498,7 +498,7 @@ static void *sev_fw_alloc(unsigned long len) {struct page *page; - page =__snp_alloc_firmware_pages (GFP_KERNEL ,get_order (len)); + page =__snp_alloc_firmware_pages (GFP_KERNEL ,get_order (len), true);if (!page)return NULL ;
Signature · 1 line
2.49.0
Tom Lendacky
maintainerQuoted 12 lines · Alexey Kardashevskiy
The
__snp_alloc_firmware_pages()helper allocates pages in the firmware state (alloc + rmpupdate). In case of failed rmpupdate, it tries reclaiming pages with already changed state. This requires calling the PSP firmware and since there issev_cmd_mutexto guard such calls, the helper takes a "locked" parameter so specify if the lock needs to be held.Most calls happen from
snp_alloc_firmware_page()which executes without the lock. Howevercommit
24512afa4336("crypto: ccp: Handle the legacy TMR allocation when SNP is enabled")
Quoted 8 lines
switched
sev_fw_alloc()fromalloc_pages()(which does not call the PSP) to__snp_alloc_firmware_pages()(which does) but did not account for the fact thatsev_fw_alloc()is called from__sev_platform_init_locked()(via__sev_platform_init_handle_tmr()) and executes with the lock held.Add a "locked" parameter to
__snp_alloc_firmware_pages().
Makesev_fw_alloc()use the new parameter to prevent potential deadlock inrmp_mark_pages_firmware()ifrmpupdate()failed.
Would it make sense to add the locked parameter to sev_fw_alloc(), too? Right now there is only one caller of sev_fw_alloc(), but in the future, if some other path should call sev_fw_alloc() and that path doesn't have the lock, then we'll miss taking it.
Thanks,
Tom
Quoted 47 lines
Fixes 24512afa4336 ("crypto: ccp: Handle the legacy TMR allocation when SNP is enabled")Signed-off-by Alexey Kardashevskiy <[email protected]>--- drivers/crypto/ccp/sev-dev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)drivers/crypto/ccp/sev-dev.c +4
-4@@ -434,7 +434,7 @@ static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, booreturn rc; } -static struct page *__snp_alloc_firmware_pages (gfp_t gfp_mask,int order) +static struct page *__snp_alloc_firmware_pages (gfp_t gfp_mask,int order,bool locked) {unsigned long npages =1ul << order, paddr;struct sev_device *sev;@@ -453,7 +453,7 @@ static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order)return page; paddr =__pa ((unsigned long )page_address (page)); -if (rmp_mark_pages_firmware (paddr, npages, false)) +if (rmp_mark_pages_firmware (paddr, npages, locked))return NULL ;return page;@@ -463,7 +463,7 @@ void *snp_alloc_firmware_page(gfp_t gfp_mask) {struct page *page; - page =__snp_alloc_firmware_pages (gfp_mask,0 ); + page =__snp_alloc_firmware_pages (gfp_mask,0 , false);return page ?page_address (page) :NULL ; }@@ -498,7 +498,7 @@ static void *sev_fw_alloc(unsigned long len) {struct page *page; - page =__snp_alloc_firmware_pages (GFP_KERNEL ,get_order (len)); + page =__snp_alloc_firmware_pages (GFP_KERNEL ,get_order (len), true);if (!page)return NULL ;
Quoted 18 lines · Tom Lendacky
Quoted 17 lines · Alexey Kardashevskiy
The
__snp_alloc_firmware_pages()helper allocates pages in the firmware state (alloc + rmpupdate). In case of failed rmpupdate, it tries reclaiming pages with already changed state. This requires calling the PSP firmware and since there issev_cmd_mutexto guard such calls, the helper takes a "locked" parameter so specify if the lock needs to be held.Most calls happen from
snp_alloc_firmware_page()which executes without the lock. Howevercommit
24512afa4336("crypto: ccp: Handle the legacy TMR allocation when SNP is enabled")switched
sev_fw_alloc()fromalloc_pages()(which does not call the PSP) to__snp_alloc_firmware_pages()(which does) but did not account for the fact thatsev_fw_alloc()is called from__sev_platform_init_locked()(via__sev_platform_init_handle_tmr()) and executes with the lock held.
Quoted 5 lines
Quoted 3 lines
Add a "locked" parameter to
__snp_alloc_firmware_pages().
Makesev_fw_alloc()use the new parameter to prevent potential deadlock inrmp_mark_pages_firmware()ifrmpupdate()failed.Would it make sense to add the locked parameter to
sev_fw_alloc(), too?
That would be another patch then, this one is a fix ;)
and I'd probably just ditch both snp_alloc_firmware_page() and sev_fw_alloc(), rename __snp_alloc_firmware_pages() to snp_alloc_firmware_page() and just use this one everywhere. Nobody needs page struct anyway, and the locking will be clear everywhere. Also do the same for snp_free_firmware_page().
It is just that snp_alloc_firmware_page() and snp_free_firmware_page() are EXPORT_SYMBOL_GPL,
Quoted 3 lines
Right now there is only one caller of
sev_fw_alloc(), but in the future, if some other path should callsev_fw_alloc()and that path doesn't have the lock, then we'll miss taking it.
I'd rather just ditch sev_fw_alloc(), does not look very useful. Thanks,
Quoted 50 lines
Thanks,
TomQuoted 47 lines
Fixes 24512afa4336 ("crypto: ccp: Handle the legacy TMR allocation when SNP is enabled")Signed-off-by Alexey Kardashevskiy <[email protected]>--- drivers/crypto/ccp/sev-dev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)drivers/crypto/ccp/sev-dev.c +4
-4@@ -434,7 +434,7 @@ static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, booreturn rc; } -static struct page *__snp_alloc_firmware_pages (gfp_t gfp_mask,int order) +static struct page *__snp_alloc_firmware_pages (gfp_t gfp_mask,int order,bool locked) {unsigned long npages =1ul << order, paddr;struct sev_device *sev;@@ -453,7 +453,7 @@ static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order)return page; paddr =__pa ((unsigned long )page_address (page)); -if (rmp_mark_pages_firmware (paddr, npages, false)) +if (rmp_mark_pages_firmware (paddr, npages, locked))return NULL ;return page;@@ -463,7 +463,7 @@ void *snp_alloc_firmware_page(gfp_t gfp_mask) {struct page *page; - page =__snp_alloc_firmware_pages (gfp_mask,0 ); + page =__snp_alloc_firmware_pages (gfp_mask,0 , false);return page ?page_address (page) :NULL ; }@@ -498,7 +498,7 @@ static void *sev_fw_alloc(unsigned long len) {struct page *page; - page =__snp_alloc_firmware_pages (GFP_KERNEL ,get_order (len)); + page =__snp_alloc_firmware_pages (GFP_KERNEL ,get_order (len), true);if (!page)return NULL ;
Signature · 1 line
Alexey
Quoted 35 lines · Alexey Kardashevskiy
Quoted 20 lines · Tom Lendacky
Quoted 19 lines · Alexey Kardashevskiy
The
__snp_alloc_firmware_pages()helper allocates pages in the firmware state (alloc + rmpupdate). In case of failed rmpupdate, it tries reclaiming pages with already changed state. This requires calling the PSP firmware and since there issev_cmd_mutexto guard such calls, the helper takes a "locked" parameter so specify if the lock needs to be held.Most calls happen from
snp_alloc_firmware_page()which executes without the lock. Howevercommit
24512afa4336("crypto: ccp: Handle the legacy TMR allocation when SNP is enabled")switched
sev_fw_alloc()fromalloc_pages()(which does not call the PSP) to__snp_alloc_firmware_pages()(which does) but did not account for the fact thatsev_fw_alloc()is called from__sev_platform_init_locked()(via__sev_platform_init_handle_tmr()) and executes with the lock held.Quoted 5 lines
Quoted 3 lines
Add a "locked" parameter to
__snp_alloc_firmware_pages().
Makesev_fw_alloc()use the new parameter to prevent potential deadlock inrmp_mark_pages_firmware()ifrmpupdate()failed.Would it make sense to add the locked parameter to
sev_fw_alloc(), too?That would be another patch then, this one is a fix ;)
and I'd probably just ditch both
snp_alloc_firmware_page()andsev_fw_alloc(), rename__snp_alloc_firmware_pages()tosnp_alloc_firmware_page()and just use this one everywhere. Nobody needs page struct anyway, and the locking will be clear everywhere. Also do the same forsnp_free_firmware_page().
Quoted 8 lines
It is just that
snp_alloc_firmware_page()andsnp_free_firmware_page()areEXPORT_SYMBOL_GPL,Quoted 3 lines
Right now there is only one caller of
sev_fw_alloc(), but in the future, if some other path should callsev_fw_alloc()and that path doesn't have the lock, then we'll miss taking it.I'd rather just ditch
sev_fw_alloc(), does not look very useful. Thanks,
Ok, I'm good with this patch for the fix. Can you also provide a follow up patch(es) to address what you've discussed?
For the fix:
Thanks,
Tom
Quoted 53 lines
Quoted 52 lines
Thanks,
TomQuoted 46 lines
Fixes 24512afa4336 ("crypto: ccp: Handle the legacy TMR allocationwhen SNP is enabled")
Signed-off-by Alexey Kardashevskiy <[email protected]>---
drivers/crypto/ccp/sev-dev.c| 8 ++++----1 file changed, 4 insertions(+), 4 deletions(-)drivers/crypto/ccp/sev-dev.c +0
-0@@ -434,7 +434,7 @@ static int rmp_mark_pages_firmware(unsigned longpaddr, unsigned int npages, boo
return rc;
}
-static struct page *__snp_alloc_firmware_pages(gfp_tgfp_mask, int order)
+static struct page *__snp_alloc_firmware_pages(gfp_tgfp_mask, int order, bool locked)
{
unsigned long npages = 1ul << order, paddr;
structsev_device*sev;drivers/crypto/ccp/sev-dev.c +3
-1@@ -453,7 +453,7 @@ static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order) return page; paddr = __pa((unsigned long)page_address(page)); - if (rmp_mark_pages_firmware(paddr, npages, false)) + if (rmp_mark_pages_firmware(paddr, npages, locked)) return NULL; return page;@@ -463,7 +463,7 @@ void *snp_alloc_firmware_page(gfp_t gfp_mask) { struct page *page; - page = __snp_alloc_firmware_pages(gfp_mask, 0); + page = __snp_alloc_firmware_pages(gfp_mask, 0, false); return page ? page_address(page) : NULL; }@@ -498,7 +498,7 @@ static void *sev_fw_alloc(unsigned long len) { struct page *page; - page = __snp_alloc_firmware_pages(GFP_KERNEL, get_order(len)); + page = __snp_alloc_firmware_pages(GFP_KERNEL, get_order(len), true); if (!page) return NULL;
Hi Alexey,
Quoted 17 lines · Alexey Kardashevskiy
The
__snp_alloc_firmware_pages()helper allocates pages in the firmware state (alloc + rmpupdate). In case of failed rmpupdate, it tries reclaiming pages with already changed state. This requires calling the PSP firmware and since there issev_cmd_mutexto guard such calls, the helper takes a "locked" parameter so specify if the lock needs to be held.Most calls happen from
snp_alloc_firmware_page()which executes without the lock. Howevercommit
24512afa4336("crypto: ccp: Handle the legacy TMR allocation when SNP is enabled")switched
sev_fw_alloc()fromalloc_pages()(which does not call the PSP) to__snp_alloc_firmware_pages()(which does) but did not account for the fact thatsev_fw_alloc()is called from__sev_platform_init_locked()(via__sev_platform_init_handle_tmr()) and executes with the lock held.
Quoted 6 lines
Add a "locked" parameter to
__snp_alloc_firmware_pages().
Makesev_fw_alloc()use the new parameter to prevent potential deadlock inrmp_mark_pages_firmware()ifrmpupdate()failed.Fixes 24512afa4336 ("crypto: ccp: Handle the legacy TMR allocation when SNP is enabled")Signed-off-by Alexey Kardashevskiy <[email protected]>
Thank you for fixing this.
I was facing a locking issue and was writing a similar patch when I discovered this!
Quoted 44 lines
--- drivers/crypto/ccp/sev-dev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)drivers/crypto/ccp/sev-dev.c +4
-4@@ -434,7 +434,7 @@ static int rmp_mark_pages_firmware(unsigned long paddr, unsigned int npages, booreturn rc; } -static struct page *__snp_alloc_firmware_pages (gfp_t gfp_mask,int order) +static struct page *__snp_alloc_firmware_pages (gfp_t gfp_mask,int order,bool locked) {unsigned long npages =1ul << order, paddr;struct sev_device *sev;@@ -453,7 +453,7 @@ static struct page *__snp_alloc_firmware_pages(gfp_t gfp_mask, int order)return page; paddr =__pa ((unsigned long )page_address (page)); -if (rmp_mark_pages_firmware (paddr, npages, false)) +if (rmp_mark_pages_firmware (paddr, npages, locked))return NULL ;return page;@@ -463,7 +463,7 @@ void *snp_alloc_firmware_page(gfp_t gfp_mask) {struct page *page; - page =__snp_alloc_firmware_pages (gfp_mask,0 ); + page =__snp_alloc_firmware_pages (gfp_mask,0 , false);return page ?page_address (page) :NULL ; }@@ -498,7 +498,7 @@ static void *sev_fw_alloc(unsigned long len) {struct page *page; - page =__snp_alloc_firmware_pages (GFP_KERNEL ,get_order (len)); + page =__snp_alloc_firmware_pages (GFP_KERNEL ,get_order (len), true);if (!page)return NULL ;
Herbert Xu
maintainerQuoted 21 lines · Alexey Kardashevskiy
The
__snp_alloc_firmware_pages()helper allocates pages in the firmware state (alloc + rmpupdate). In case of failed rmpupdate, it tries reclaiming pages with already changed state. This requires calling the PSP firmware and since there issev_cmd_mutexto guard such calls, the helper takes a "locked" parameter so specify if the lock needs to be held.Most calls happen from
snp_alloc_firmware_page()which executes without the lock. Howevercommit
24512afa4336("crypto: ccp: Handle the legacy TMR allocation when SNP is enabled")switched
sev_fw_alloc()fromalloc_pages()(which does not call the PSP) to__snp_alloc_firmware_pages()(which does) but did not account for the fact thatsev_fw_alloc()is called from__sev_platform_init_locked()(via__sev_platform_init_handle_tmr()) and executes with the lock held.Add a "locked" parameter to
__snp_alloc_firmware_pages().
Makesev_fw_alloc()use the new parameter to prevent potential deadlock inrmp_mark_pages_firmware()ifrmpupdate()failed.
Quoted 5 lines
Fixes 24512afa4336 ("crypto: ccp: Handle the legacy TMR allocation when SNP is enabled")Signed-off-by Alexey Kardashevskiy <[email protected]>--- drivers/crypto/ccp/sev-dev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
Patch applied. Thanks.
Signature · 3 lines
Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt