lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 21 Oct 2022 13:48:14 -0700
From:   Mike Kravetz <mike.kravetz@...cle.com>
To:     Rik van Riel <riel@...riel.com>
Cc:     linux-kernel@...r.kernel.org, linux-mm@...ck.org,
        kernel-team@...a.com, Andrew Morton <akpm@...ux-foundation.org>,
        David Hildenbrand <david@...hat.com>
Subject: Re: [PATCH] mm,madvise,hugetlb: fix unexpected data loss with
 MADV_DONTNEED on hugetlbfs

On 10/21/22 15:45, Rik van Riel wrote:
> A common use case for hugetlbfs is for the application to create
> memory pools backed by huge pages, which then get handed over to
> some malloc library (eg. jemalloc) for further management.
> 
> That malloc library may be doing MADV_DONTNEED calls on memory
> that is no longer needed, expecting those calls to happen on
> PAGE_SIZE boundaries.
> 

Thanks Rik.  I tend to agree with this direction as it is 'breaking'
current code.  David and I discussed this in this thread,
https://lore.kernel.org/linux-mm/356a4b9a-1f56-ae06-b211-bd32fc93ecda@redhat.com/

One thing to note is that there was not any documentation saying
madvise would happen on page boundaries.  The system call takes a
length and rounds up to page size.  However, the man page explicitly
said it operates on a byte range.  Certainly mm people and others
know we only operate on pages.  But, that is not what was documented.

When the change was made to add hugetlb support, the decision was made
to round up the range to hugetlb page boundaries in hugetlb vmas.  This
was to be consistent with how madvise operated on base pages.  At the
same time, madvise documentation was updated say it operates on page
boundaries as well as the behavior for hugetlb mappings.  If moving
forward with this change we will need to update the man page.
-- 
Mike Kravetz

> However, currently the MADV_DONTNEED code rounds up any such
> requests to HPAGE_PMD_SIZE boundaries. This leads to undesired
> outcomes when jemalloc expects a 4kB MADV_DONTNEED, but 2MB of
> memory get zeroed out, instead.
> 
> Use of pre-built shared libraries means that user code does not
> always know the page size of every memory arena in use.
> 
> Avoid unexpected data loss with MADV_DONTNEED by rounding up
> only to PAGE_SIZE (in do_madvise), and rounding down to huge
> page granularity.
> 
> That way programs will only get as much memory zeroed out as
> they requested.
> 
> While we're here, refactor madvise_dontneed_free_valid_vma
> a little so mlocked hugetlb VMAs need MADV_DONTNEED_LOCKED.
> 
> Cc: Mike Kravetz <mike.kravetz@...cle.com>
> Cc: Andrew Morton <akpm@...ux-foundation.org>
> Cc: David Hildenbrand <david@...hat.com>
> Fixes: 90e7e7f5ef3f ("mm: enable MADV_DONTNEED for hugetlb mappings")
> ---
>  mm/madvise.c | 26 +++++++++++++++++++-------
>  1 file changed, 19 insertions(+), 7 deletions(-)
> 
> diff --git a/mm/madvise.c b/mm/madvise.c
> index 2baa93ca2310..a60e8e23c323 100644
> --- a/mm/madvise.c
> +++ b/mm/madvise.c
> @@ -799,21 +799,29 @@ static bool madvise_dontneed_free_valid_vma(struct vm_area_struct *vma,
>  					    unsigned long *end,
>  					    int behavior)
>  {
> -	if (!is_vm_hugetlb_page(vma)) {
> -		unsigned int forbidden = VM_PFNMAP;
> +	unsigned int forbidden = VM_PFNMAP;
>  
> -		if (behavior != MADV_DONTNEED_LOCKED)
> -			forbidden |= VM_LOCKED;
> +	if (behavior != MADV_DONTNEED_LOCKED)
> +		forbidden |= VM_LOCKED;
>  
> -		return !(vma->vm_flags & forbidden);
> -	}
> +	if (vma->vm_flags & forbidden)
> +		return false;
> +
> +	if (!is_vm_hugetlb_page(vma))
> +		return true;
>  
>  	if (behavior != MADV_DONTNEED && behavior != MADV_DONTNEED_LOCKED)
>  		return false;
>  	if (start & ~huge_page_mask(hstate_vma(vma)))
>  		return false;
>  
> -	*end = ALIGN(*end, huge_page_size(hstate_vma(vma)));
> +	/*
> +	 * Madvise callers expect the length to be rounded up to the page
> +	 * size, but they may not know the page size for this VMA is larger
> +	 * than PAGE_SIZE! Round down huge pages to avoid unexpected data loss.
> +	 */
> +	*end = ALIGN_DOWN(*end, huge_page_size(hstate_vma(vma)));
> +
>  	return true;
>  }
>  
> @@ -828,6 +836,10 @@ static long madvise_dontneed_free(struct vm_area_struct *vma,
>  	if (!madvise_dontneed_free_valid_vma(vma, start, &end, behavior))
>  		return -EINVAL;
>  
> +	/* A small MADV_DONTNEED on a huge page gets rounded down to zero. */
> +	if (start == end)
> +		return 0;
> +
>  	if (!userfaultfd_remove(vma, start, end)) {
>  		*prev = NULL; /* mmap_lock has been dropped, prev is stale */
>  
> -- 
> 2.37.2
> 
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ