[PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check

Lorenzo Stoakes posted 5 patches 2 months, 1 week ago
[PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by Lorenzo Stoakes 2 months, 1 week ago
The check_mm_seal() function is doing something general - checking whether
a range contains only VMAs (or rather that it does NOT contain any
unmapped regions).

So rename this function to range_contains_unmapped().

Additionally simplify the logic, we are simply checking whether the last
vma->vm_end has either a VMA starting after it or ends before the end
parameter.

This check is rather dubious, so it is sensible to keep it local to
mm/mseal.c as at a later stage it may be removed, and we don't want any
other mm code to perform such a check.

No functional change intended.

Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>
Acked-by: David Hildenbrand <david@redhat.com>
---
 mm/mseal.c | 39 ++++++++++++++++-----------------------
 1 file changed, 16 insertions(+), 23 deletions(-)

diff --git a/mm/mseal.c b/mm/mseal.c
index adbcc65e9660..1059322add34 100644
--- a/mm/mseal.c
+++ b/mm/mseal.c
@@ -38,31 +38,28 @@ static int mseal_fixup(struct vma_iterator *vmi, struct vm_area_struct *vma,
 }
 
 /*
- * Check for do_mseal:
- * 1> start is part of a valid vma.
- * 2> end is part of a valid vma.
- * 3> No gap (unallocated address) between start and end.
- * 4> map is sealable.
+ * Does the [start, end) range contain any unmapped memory?
+ *
+ * We ensure that:
+ * - start is part of a valid VMA.
+ * - end is part of a valid VMA.
+ * - no gap (unallocated memory) exists between start and end.
  */
-static int check_mm_seal(unsigned long start, unsigned long end)
+static bool range_contains_unmapped(struct mm_struct *mm,
+		unsigned long start, unsigned long end)
 {
 	struct vm_area_struct *vma;
-	unsigned long nstart = start;
+	unsigned long prev_end = start;
 	VMA_ITERATOR(vmi, current->mm, start);
 
-	/* going through each vma to check. */
 	for_each_vma_range(vmi, vma, end) {
-		if (vma->vm_start > nstart)
-			/* unallocated memory found. */
-			return -ENOMEM;
-
-		if (vma->vm_end >= end)
-			return 0;
+		if (vma->vm_start > prev_end)
+			return true;
 
-		nstart = vma->vm_end;
+		prev_end = vma->vm_end;
 	}
 
-	return -ENOMEM;
+	return prev_end < end;
 }
 
 /*
@@ -184,14 +181,10 @@ int do_mseal(unsigned long start, size_t len_in, unsigned long flags)
 	if (mmap_write_lock_killable(mm))
 		return -EINTR;
 
-	/*
-	 * First pass, this helps to avoid
-	 * partial sealing in case of error in input address range,
-	 * e.g. ENOMEM error.
-	 */
-	ret = check_mm_seal(start, end);
-	if (ret)
+	if (range_contains_unmapped(mm, start, end)) {
+		ret = -ENOMEM;
 		goto out;
+	}
 
 	/*
 	 * Second pass, this should success, unless there are errors
-- 
2.50.1
Re: [PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by Pedro Falcato 2 months, 1 week ago
On Fri, Jul 25, 2025 at 09:29:44AM +0100, Lorenzo Stoakes wrote:
> The check_mm_seal() function is doing something general - checking whether
> a range contains only VMAs (or rather that it does NOT contain any
> unmapped regions).
> 
> So rename this function to range_contains_unmapped().
> 
> Additionally simplify the logic, we are simply checking whether the last
> vma->vm_end has either a VMA starting after it or ends before the end
> parameter.
> 
> This check is rather dubious, so it is sensible to keep it local to
> mm/mseal.c as at a later stage it may be removed, and we don't want any
> other mm code to perform such a check.
> 
> No functional change intended.
> 
> Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>
> Acked-by: David Hildenbrand <david@redhat.com>

Reviewed-by: Pedro Falcato <pfalcato@suse.de>

-- 
Pedro
Re: [PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by Lorenzo Stoakes 2 months, 1 week ago
Hi Andrew,

Can you apply the attached trivial fix-patch which adds a clarifying comment to
mm/mseal.c.

Thanks, Lorenzo


----8<----
From bf8211317183353b3652baac1af1d35555733d2b Mon Sep 17 00:00:00 2001
From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Date: Fri, 25 Jul 2025 19:23:50 +0100
Subject: [PATCH] mm/mseal: add comment explaining why we disallow gaps on
 mseal()

This explains the semantics clearly, the 'why' of the situation.

Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
 mm/mseal.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/mm/mseal.c b/mm/mseal.c
index 1059322add34..d140f569c4c3 100644
--- a/mm/mseal.c
+++ b/mm/mseal.c
@@ -37,6 +37,18 @@ static int mseal_fixup(struct vma_iterator *vmi, struct vm_area_struct *vma,
 	return ret;
 }

+/*
+ * mseal() disallows an input range which contain unmapped ranges (VMA holes).
+ *
+ * It disallows unmapped regions from start to end whether they exist at the
+ * start, in the middle, or at the end of the range, or any combination thereof.
+ *
+ * This is because after sealng a range, there's nothing to stop memory mapping
+ * of ranges in the remaining gaps later, meaning that the user might then
+ * wrongly consider the entirety of the mseal()'d range to be sealed when it
+ * in fact isn't.
+ */
+
 /*
  * Does the [start, end) range contain any unmapped memory?
  *
--
2.50.1
Re: [PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by Jeff Xu 2 months, 1 week ago
Hi Lorenzo,

On Fri, Jul 25, 2025 at 1:30 AM Lorenzo Stoakes
<lorenzo.stoakes@oracle.com> wrote:
>
> The check_mm_seal() function is doing something general - checking whether
> a range contains only VMAs (or rather that it does NOT contain any
> unmapped regions).
>
> So rename this function to range_contains_unmapped().
>
Thanks for keeping the comments.

In the prior version of this patch, I requested that we keep the
check_mm_seal()  and its comments. And this version keeps the comments
but removes the check_mm_seal() name.

As I said, check_mm_seal() with its comments is a contract for
entry-check for mseal().  My understanding is that you are going to
move range_contains_unmapped() to vma.c. When that happens, mseal()
will lose this entry-check contract.

Contact is a great way to hide implementation details. Could you
please keep check_mm_seal() in mseal.c and create
range_contains_unmapped() in vma.c. Then you can refactor as needed.

Thanks and regards,
-Jeff





> Additionally simplify the logic, we are simply checking whether the last
> vma->vm_end has either a VMA starting after it or ends before the end
> parameter.
>
> This check is rather dubious, so it is sensible to keep it local to
> mm/mseal.c as at a later stage it may be removed, and we don't want any
> other mm code to perform such a check.
>
> No functional change intended.
>
> Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
> Reviewed-by: Liam R. Howlett <Liam.Howlett@oracle.com>
> Acked-by: David Hildenbrand <david@redhat.com>
> ---
>  mm/mseal.c | 39 ++++++++++++++++-----------------------
>  1 file changed, 16 insertions(+), 23 deletions(-)
>
> diff --git a/mm/mseal.c b/mm/mseal.c
> index adbcc65e9660..1059322add34 100644
> --- a/mm/mseal.c
> +++ b/mm/mseal.c
> @@ -38,31 +38,28 @@ static int mseal_fixup(struct vma_iterator *vmi, struct vm_area_struct *vma,
>  }
>
>  /*
> - * Check for do_mseal:
> - * 1> start is part of a valid vma.
> - * 2> end is part of a valid vma.
> - * 3> No gap (unallocated address) between start and end.
> - * 4> map is sealable.
> + * Does the [start, end) range contain any unmapped memory?
> + *
> + * We ensure that:
> + * - start is part of a valid VMA.
> + * - end is part of a valid VMA.
> + * - no gap (unallocated memory) exists between start and end.
>   */
> -static int check_mm_seal(unsigned long start, unsigned long end)
> +static bool range_contains_unmapped(struct mm_struct *mm,
> +               unsigned long start, unsigned long end)
>  {
>         struct vm_area_struct *vma;
> -       unsigned long nstart = start;
> +       unsigned long prev_end = start;
>         VMA_ITERATOR(vmi, current->mm, start);
>
> -       /* going through each vma to check. */
>         for_each_vma_range(vmi, vma, end) {
> -               if (vma->vm_start > nstart)
> -                       /* unallocated memory found. */
> -                       return -ENOMEM;
> -
> -               if (vma->vm_end >= end)
> -                       return 0;
> +               if (vma->vm_start > prev_end)
> +                       return true;
>
> -               nstart = vma->vm_end;
> +               prev_end = vma->vm_end;
>         }
>
> -       return -ENOMEM;
> +       return prev_end < end;
>  }
>
>  /*
> @@ -184,14 +181,10 @@ int do_mseal(unsigned long start, size_t len_in, unsigned long flags)
>         if (mmap_write_lock_killable(mm))
>                 return -EINTR;
>
> -       /*
> -        * First pass, this helps to avoid
> -        * partial sealing in case of error in input address range,
> -        * e.g. ENOMEM error.
> -        */
> -       ret = check_mm_seal(start, end);
> -       if (ret)
> +       if (range_contains_unmapped(mm, start, end)) {
> +               ret = -ENOMEM;
>                 goto out;
> +       }
>
>         /*
>          * Second pass, this should success, unless there are errors
> --
> 2.50.1
>
Re: [PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by Lorenzo Stoakes 2 months, 1 week ago
On Fri, Jul 25, 2025 at 10:30:08AM -0700, Jeff Xu wrote:
> Hi Lorenzo,
>
> On Fri, Jul 25, 2025 at 1:30 AM Lorenzo Stoakes
> <lorenzo.stoakes@oracle.com> wrote:
> >
> > The check_mm_seal() function is doing something general - checking whether
> > a range contains only VMAs (or rather that it does NOT contain any
> > unmapped regions).
> >
> > So rename this function to range_contains_unmapped().
> >
> Thanks for keeping the comments.

You're welcome.

>
> In the prior version of this patch, I requested that we keep the
> check_mm_seal()  and its comments. And this version keeps the comments
> but removes the check_mm_seal() name.

I didn't catch that being your request.

>
> As I said, check_mm_seal() with its comments is a contract for
> entry-check for mseal().  My understanding is that you are going to
> move range_contains_unmapped() to vma.c. When that happens, mseal()
> will lose this entry-check contract.

This is just bizarre.

Code doesn't stop working if you put it in another function.

And you're now reviewing me for stuff I haven't done? :P

>
> Contact is a great way to hide implementation details. Could you
> please keep check_mm_seal() in mseal.c and create
> range_contains_unmapped() in vma.c. Then you can refactor as needed.

Wait what?

OK maybe now I see what you mean, you want a function that just wraps
range_contains_unmapped() with a comment explaining the 'contract'.

range_contains_unmapped() enforces your required contract and the comments
make it extremely explicit, so this is not a reasonable request, sorry.
Re: [PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by David Hildenbrand 2 months, 1 week ago
On 25.07.25 19:43, Lorenzo Stoakes wrote:
> On Fri, Jul 25, 2025 at 10:30:08AM -0700, Jeff Xu wrote:
>> Hi Lorenzo,
>>
>> On Fri, Jul 25, 2025 at 1:30 AM Lorenzo Stoakes
>> <lorenzo.stoakes@oracle.com> wrote:
>>>
>>> The check_mm_seal() function is doing something general - checking whether
>>> a range contains only VMAs (or rather that it does NOT contain any
>>> unmapped regions).
>>>
>>> So rename this function to range_contains_unmapped().
>>>
>> Thanks for keeping the comments.
> 
> You're welcome.
> 
>>
>> In the prior version of this patch, I requested that we keep the
>> check_mm_seal()  and its comments. And this version keeps the comments
>> but removes the check_mm_seal() name.
> 
> I didn't catch that being your request.
> 
>>
>> As I said, check_mm_seal() with its comments is a contract for
>> entry-check for mseal().  My understanding is that you are going to
>> move range_contains_unmapped() to vma.c. When that happens, mseal()
>> will lose this entry-check contract.
> 
> This is just bizarre.
> 
> Code doesn't stop working if you put it in another function.
> 
> And you're now reviewing me for stuff I haven't done? :P
> 
>>
>> Contact is a great way to hide implementation details. Could you
>> please keep check_mm_seal() in mseal.c and create
>> range_contains_unmapped() in vma.c. Then you can refactor as needed.
> 
> Wait what?

do_mseal() calls range_contains_unmapped(), so I don't see the problem.

We could add a comment above the range_contains_unmapped(), call stating 
*why* we do that, which is much more relevant than some check_XXX function.

/*
  * mseal() is documented to reject ranges that contain unmapped ranges
  * (VMA holes): we can only seal VMAs, so nothing would stop mmap() etc.
  * from succeeding on these unmapped ranged later, and we would not
  * actually be sealing the requested range.
  */

Something like that.

-- 
Cheers,

David / dhildenb

Re: [PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by Jeff Xu 2 months, 1 week ago
On Fri, Jul 25, 2025 at 11:10 AM David Hildenbrand <david@redhat.com> wrote:
>
> On 25.07.25 19:43, Lorenzo Stoakes wrote:
> > On Fri, Jul 25, 2025 at 10:30:08AM -0700, Jeff Xu wrote:
> >> Hi Lorenzo,
> >>
> >> On Fri, Jul 25, 2025 at 1:30 AM Lorenzo Stoakes
> >> <lorenzo.stoakes@oracle.com> wrote:
> >>>
> >>> The check_mm_seal() function is doing something general - checking whether
> >>> a range contains only VMAs (or rather that it does NOT contain any
> >>> unmapped regions).
> >>>
> >>> So rename this function to range_contains_unmapped().
> >>>
> >> Thanks for keeping the comments.
> >
> > You're welcome.
> >
> >>
> >> In the prior version of this patch, I requested that we keep the
> >> check_mm_seal()  and its comments. And this version keeps the comments
> >> but removes the check_mm_seal() name.
> >
> > I didn't catch that being your request.
> >
> >>
> >> As I said, check_mm_seal() with its comments is a contract for
> >> entry-check for mseal().  My understanding is that you are going to
> >> move range_contains_unmapped() to vma.c. When that happens, mseal()
> >> will lose this entry-check contract.
> >
> > This is just bizarre.
> >
> > Code doesn't stop working if you put it in another function.
> >
> > And you're now reviewing me for stuff I haven't done? :P
> >
> >>
> >> Contact is a great way to hide implementation details. Could you
> >> please keep check_mm_seal() in mseal.c and create
> >> range_contains_unmapped() in vma.c. Then you can refactor as needed.
> >
> > Wait what?
>
> do_mseal() calls range_contains_unmapped(), so I don't see the problem.
>
> We could add a comment above the range_contains_unmapped(), call stating
> *why* we do that, which is much more relevant than some check_XXX function.
>
> /*
>   * mseal() is documented to reject ranges that contain unmapped ranges
>   * (VMA holes): we can only seal VMAs, so nothing would stop mmap() etc.
>   * from succeeding on these unmapped ranged later, and we would not
>   * actually be sealing the requested range.
>   */
>
Adding a reason explaining the reason is way more helpful than just
stating what it's doing. Thanks!

a nit: I would use:

> /*
>   * mseal() is documented to reject ranges that contain unmapped ranges
>   * (VMA holes in the middle or both ends): we can only seal VMAs, so nothing
>   * would stop mmap() etc. from succeeding on these unmapped ranged later, and
>   * we would not actually be sealing the requested range.
>   */

To make it clear to the reader, because VMA holes might lead people to
think they're only in the middle.

Thanks and regards,
-Jeff





> Something like that.
>
> --
> Cheers,
>
> David / dhildenb
>
Re: [PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by Lorenzo Stoakes 2 months, 1 week ago
On Fri, Jul 25, 2025 at 08:10:11PM +0200, David Hildenbrand wrote:
> On 25.07.25 19:43, Lorenzo Stoakes wrote:
> > >
> > > Contact is a great way to hide implementation details. Could you
> > > please keep check_mm_seal() in mseal.c and create
> > > range_contains_unmapped() in vma.c. Then you can refactor as needed.
> >
> > Wait what?
>
> do_mseal() calls range_contains_unmapped(), so I don't see the problem.

Thanks.

>
> We could add a comment above the range_contains_unmapped(), call stating
> *why* we do that, which is much more relevant than some check_XXX function.
>
> /*
>  * mseal() is documented to reject ranges that contain unmapped ranges
>  * (VMA holes): we can only seal VMAs, so nothing would stop mmap() etc.
>  * from succeeding on these unmapped ranged later, and we would not
>  * actually be sealing the requested range.
>  */
>
> Something like that.

Actually this is useful, as it explains to me why we disallow gaps (which I
found silly).

Though I'm not sure I still agree with this (under what circumstances
exactly you'd map within an mseal()'d range afterwards and then assume that
mapping is sealed, I don't know).

Anyway that's sort of besides the point.

I'll send a fix-patch to include this.

>
> --
> Cheers,
>
> David / dhildenb
>

Cheers, Lorenzo
Re: [PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by Jeff Xu 2 months, 1 week ago
Hi Lorenzo

On Fri, Jul 25, 2025 at 10:43 AM Lorenzo Stoakes
<lorenzo.stoakes@oracle.com> wrote:
>
> On Fri, Jul 25, 2025 at 10:30:08AM -0700, Jeff Xu wrote:
> > Hi Lorenzo,
> >
> > On Fri, Jul 25, 2025 at 1:30 AM Lorenzo Stoakes
> > <lorenzo.stoakes@oracle.com> wrote:
> > >
> > > The check_mm_seal() function is doing something general - checking whether
> > > a range contains only VMAs (or rather that it does NOT contain any
> > > unmapped regions).
> > >
> > > So rename this function to range_contains_unmapped().
> > >
> > Thanks for keeping the comments.
>
> You're welcome.
>
> >
> > In the prior version of this patch, I requested that we keep the
> > check_mm_seal()  and its comments. And this version keeps the comments
> > but removes the check_mm_seal() name.
>
> I didn't catch that being your request.
>
> >
> > As I said, check_mm_seal() with its comments is a contract for
> > entry-check for mseal().  My understanding is that you are going to
> > move range_contains_unmapped() to vma.c. When that happens, mseal()
> > will lose this entry-check contract.
>
> This is just bizarre.
>
> Code doesn't stop working if you put it in another function.
>
> And you're now reviewing me for stuff I haven't done? :P
>
> >
> > Contact is a great way to hide implementation details. Could you
> > please keep check_mm_seal() in mseal.c and create
> > range_contains_unmapped() in vma.c. Then you can refactor as needed.
>
> Wait what?
>
> OK maybe now I see what you mean, you want a function that just wraps
> range_contains_unmapped() with a comment explaining the 'contract'.
>
Yes. You can view it that way from an implementation point of view.

Contract mainly serves as a way to help design and abstract the code.

> range_contains_unmapped() enforces your required contract and the comments
> make it extremely explicit, so this is not a reasonable request, sorry.

Technically, this contract belongs to mseal, but if you have strong
opinions on this, that's fine, as long as range_contains_unmapped()
doesn't accidentally remove those comments in the future, which I'm
sure you won't.

Acked-by: Jeff Xu <jeffxu@chromium.org>

Thanks and regards,
-Jeff
Re: [PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by Pedro Falcato 2 months, 1 week ago
On Fri, Jul 25, 2025 at 11:09:13AM -0700, Jeff Xu wrote:
> Hi Lorenzo
> 
> On Fri, Jul 25, 2025 at 10:43 AM Lorenzo Stoakes
> <lorenzo.stoakes@oracle.com> wrote:
> >
> > On Fri, Jul 25, 2025 at 10:30:08AM -0700, Jeff Xu wrote:
> > > Hi Lorenzo,
> > >
> > > On Fri, Jul 25, 2025 at 1:30 AM Lorenzo Stoakes
> > > <lorenzo.stoakes@oracle.com> wrote:
> > > >
> > > > The check_mm_seal() function is doing something general - checking whether
> > > > a range contains only VMAs (or rather that it does NOT contain any
> > > > unmapped regions).
> > > >
> > > > So rename this function to range_contains_unmapped().
> > > >
> > > Thanks for keeping the comments.
> >
> > You're welcome.
> >
> > >
> > > In the prior version of this patch, I requested that we keep the
> > > check_mm_seal()  and its comments. And this version keeps the comments
> > > but removes the check_mm_seal() name.
> >
> > I didn't catch that being your request.
> >
> > >
> > > As I said, check_mm_seal() with its comments is a contract for
> > > entry-check for mseal().  My understanding is that you are going to
> > > move range_contains_unmapped() to vma.c. When that happens, mseal()
> > > will lose this entry-check contract.
> >
> > This is just bizarre.
> >
> > Code doesn't stop working if you put it in another function.
> >
> > And you're now reviewing me for stuff I haven't done? :P
> >
> > >
> > > Contact is a great way to hide implementation details. Could you
> > > please keep check_mm_seal() in mseal.c and create
> > > range_contains_unmapped() in vma.c. Then you can refactor as needed.
> >
> > Wait what?
> >
> > OK maybe now I see what you mean, you want a function that just wraps
> > range_contains_unmapped() with a comment explaining the 'contract'.
> >
> Yes. You can view it that way from an implementation point of view.
> 
> Contract mainly serves as a way to help design and abstract the code.
>

What code? This is an extremely simple file. We don't need deep design
and abstractions here.

> > range_contains_unmapped() enforces your required contract and the comments
> > make it extremely explicit, so this is not a reasonable request, sorry.
> 
> Technically, this contract belongs to mseal, but if you have strong
> opinions on this, that's fine, as long as range_contains_unmapped()
> doesn't accidentally remove those comments in the future, which I'm
> sure you won't.
> 

As far as I'm concerned, mseal() has little to no contract - we still don't have
a solid definition of what mseal() is supposed to do, things are still fluctuating,
and there's no man page (and no one is going to look into random kernel comments
for this).

FTR: I entirely plan on axing this function in the near future (or will try to).
There's no valid reason for this to exist, and it's causing extra burden on the
implementation - besides serving as a poor example for future mmap-ish syscalls.

-- 
Pedro
Re: [PATCH v4 4/5] mm/mseal: simplify and rename VMA gap check
Posted by Lorenzo Stoakes 2 months, 1 week ago
On Fri, Jul 25, 2025 at 11:09:13AM -0700, Jeff Xu wrote:
> Hi Lorenzo
>
> On Fri, Jul 25, 2025 at 10:43 AM Lorenzo Stoakes
> > OK maybe now I see what you mean, you want a function that just wraps
> > range_contains_unmapped() with a comment explaining the 'contract'.
> >
> Yes. You can view it that way from an implementation point of view.
>
> Contract mainly serves as a way to help design and abstract the code.

Right sure, I sort of good the idea, I just think it's a bit OTT for this
check whose contract is already clearly stated in code.

>
> > range_contains_unmapped() enforces your required contract and the comments
> > make it extremely explicit, so this is not a reasonable request, sorry.
>
> Technically, this contract belongs to mseal, but if you have strong
> opinions on this, that's fine, as long as range_contains_unmapped()
> doesn't accidentally remove those comments in the future, which I'm
> sure you won't.

We won't change the semantics without a specific patch suggesting to do so,
which you and Kees will be cc'd on!

I care very much about making sure we get the mechanics of mseal() right,
so I'm not going to allow such changes unless we sensibly reach agreement
that it's the right way forward (i.e. the same obviously as if we chose to
_change_ a contract formulation using your approach).


>
> Acked-by: Jeff Xu <jeffxu@chromium.org>

Thanks, appreciated!

>
> Thanks and regards,
> -Jeff

Cheers, Lorenzo