diff mbox

x86/memshr: properly check grant references

Message ID 5829A143020000780011E59D@prv-mh.provo.novell.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Beulich Nov. 14, 2016, 10:34 a.m. UTC
They need to be range checked against the current table limit in any
event.

Reported-by: Huawei PSIRT <psirt@huawei.com>

Move the code to where it belongs, eliminating a number of duplicate
definitions. Add locking. Produce proper error codes, and consume them
instead of making one up. Check grant type. Convert parameter types at
once.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
Note that likely there's more work needed subsequently: The grant isn't
being marked in use for the duration of the use of the GFN. But I'll
leave that to someone actually knowing how to properly to test this.
x86/memshr: properly check grant references

They need to be range checked against the current table limit in any
event.

Reported-by: Huawei PSIRT <psirt@huawei.com>

Move the code to where it belongs, eliminating a number of duplicate
definitions. Add locking. Produce proper error codes, and consume them
instead of making one up. Check grant type. Convert parameter types at
once.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
Note that likely there's more work needed subsequently: The grant isn't
being marked in use for the duration of the use of the GFN. But I'll
leave that to someone actually knowing how to properly to test this.

--- a/xen/arch/x86/mm/mem_sharing.c
+++ b/xen/arch/x86/mm/mem_sharing.c
@@ -753,75 +753,25 @@ int mem_sharing_debug_gfn(struct domain
     return num_refs;
 }
 
-#define SHGNT_PER_PAGE_V1 (PAGE_SIZE / sizeof(grant_entry_v1_t))
-#define shared_entry_v1(t, e) \
-    ((t)->shared_v1[(e)/SHGNT_PER_PAGE_V1][(e)%SHGNT_PER_PAGE_V1])
-#define SHGNT_PER_PAGE_V2 (PAGE_SIZE / sizeof(grant_entry_v2_t))
-#define shared_entry_v2(t, e) \
-    ((t)->shared_v2[(e)/SHGNT_PER_PAGE_V2][(e)%SHGNT_PER_PAGE_V2])
-#define STGNT_PER_PAGE (PAGE_SIZE / sizeof(grant_status_t))
-#define status_entry(t, e) \
-    ((t)->status[(e)/STGNT_PER_PAGE][(e)%STGNT_PER_PAGE])
-
-static grant_entry_header_t *
-shared_entry_header(struct grant_table *t, grant_ref_t ref)
-{
-    ASSERT (t->gt_version != 0);
-    if ( t->gt_version == 1 )
-        return (grant_entry_header_t*)&shared_entry_v1(t, ref);
-    else
-        return &shared_entry_v2(t, ref).hdr;
-}
-
-static int mem_sharing_gref_to_gfn(struct domain *d, 
-                                   grant_ref_t ref, 
-                                   unsigned long *gfn)
-{
-    if ( d->grant_table->gt_version < 1 )
-        return -1;
-
-    if ( d->grant_table->gt_version == 1 ) 
-    {
-        grant_entry_v1_t *sha1;
-        sha1 = &shared_entry_v1(d->grant_table, ref);
-        *gfn = sha1->frame;
-    } 
-    else 
-    {
-        grant_entry_v2_t *sha2;
-        sha2 = &shared_entry_v2(d->grant_table, ref);
-        *gfn = sha2->full_page.frame;
-    }
- 
-    return 0;
-}
-
-
 int mem_sharing_debug_gref(struct domain *d, grant_ref_t ref)
 {
-    grant_entry_header_t *shah;
+    int rc;
     uint16_t status;
-    unsigned long gfn;
+    gfn_t gfn;
 
-    if ( d->grant_table->gt_version < 1 )
+    rc = mem_sharing_gref_to_gfn(d->grant_table, ref, &gfn, &status);
+    if ( rc )
     {
-        MEM_SHARING_DEBUG( 
-                "Asked to debug [dom=%d,gref=%d], but not yet inited.\n",
-                d->domain_id, ref);
-        return -EINVAL;
-    }
-    (void)mem_sharing_gref_to_gfn(d, ref, &gfn); 
-    shah = shared_entry_header(d->grant_table, ref);
-    if ( d->grant_table->gt_version == 1 ) 
-        status = shah->flags;
-    else 
-        status = status_entry(d->grant_table, ref);
+        MEM_SHARING_DEBUG("Asked to debug [dom=%d,gref=%u]: error %d.\n",
+                          d->domain_id, ref, rc);
+        return rc;
+    }
     
     MEM_SHARING_DEBUG(
             "==> Grant [dom=%d,ref=%d], status=%x. ", 
             d->domain_id, ref, status);
 
-    return mem_sharing_debug_gfn(d, gfn); 
+    return mem_sharing_debug_gfn(d, gfn_x(gfn));
 }
 
 int mem_sharing_nominate_page(struct domain *d,
@@ -1422,23 +1372,24 @@ int mem_sharing_memop(XEN_GUEST_HANDLE_P
         case XENMEM_sharing_op_nominate_gref:
         {
             grant_ref_t gref = mso.u.nominate.u.grant_ref;
-            unsigned long gfn;
+            gfn_t gfn;
             shr_handle_t handle;
 
             rc = -EINVAL;
             if ( !mem_sharing_enabled(d) )
                 goto out;
-            if ( mem_sharing_gref_to_gfn(d, gref, &gfn) < 0 )
+            rc = mem_sharing_gref_to_gfn(d->grant_table, gref, &gfn, NULL);
+            if ( rc < 0 )
                 goto out;
 
-            rc = mem_sharing_nominate_page(d, gfn, 3, &handle);
+            rc = mem_sharing_nominate_page(d, gfn_x(gfn), 3, &handle);
             mso.u.nominate.handle = handle;
         }
         break;
 
         case XENMEM_sharing_op_share:
         {
-            unsigned long sgfn, cgfn;
+            gfn_t sgfn, cgfn;
             struct domain *cd;
             shr_handle_t sh, ch;
 
@@ -1470,35 +1421,38 @@ int mem_sharing_memop(XEN_GUEST_HANDLE_P
                 grant_ref_t gref = (grant_ref_t) 
                                     (XENMEM_SHARING_OP_FIELD_GET_GREF(
                                         mso.u.share.source_gfn));
-                if ( mem_sharing_gref_to_gfn(d, gref, &sgfn) < 0 )
+                rc = mem_sharing_gref_to_gfn(d->grant_table, gref, &sgfn,
+                                             NULL);
+                if ( rc < 0 )
                 {
                     rcu_unlock_domain(cd);
-                    rc = -EINVAL;
                     goto out;
                 }
-            } else {
-                sgfn  = mso.u.share.source_gfn;
             }
+            else
+                sgfn = _gfn(mso.u.share.source_gfn);
 
             if ( XENMEM_SHARING_OP_FIELD_IS_GREF(mso.u.share.client_gfn) )
             {
                 grant_ref_t gref = (grant_ref_t) 
                                     (XENMEM_SHARING_OP_FIELD_GET_GREF(
                                         mso.u.share.client_gfn));
-                if ( mem_sharing_gref_to_gfn(cd, gref, &cgfn) < 0 )
+                rc = mem_sharing_gref_to_gfn(cd->grant_table, gref, &cgfn,
+                                             NULL);
+                if ( rc < 0 )
                 {
                     rcu_unlock_domain(cd);
-                    rc = -EINVAL;
                     goto out;
                 }
-            } else {
-                cgfn  = mso.u.share.client_gfn;
             }
+            else
+                cgfn = _gfn(mso.u.share.client_gfn);
 
             sh = mso.u.share.source_handle;
             ch = mso.u.share.client_handle;
 
-            rc = mem_sharing_share_pages(d, sgfn, sh, cd, cgfn, ch); 
+            rc = mem_sharing_share_pages(d, gfn_x(sgfn), sh,
+                                         cd, gfn_x(cgfn), ch);
 
             rcu_unlock_domain(cd);
         }
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -3438,6 +3438,53 @@ void grant_table_init_vcpu(struct vcpu *
     v->maptrack_tail = MAPTRACK_TAIL;
 }
 
+#ifdef CONFIG_HAS_MEM_SHARING
+int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
+                            gfn_t *gfn, uint16_t *status)
+{
+    int rc = 0;
+    uint16_t flags = 0;
+
+    grant_read_lock(gt);
+
+    if ( gt->gt_version < 1 )
+        rc = -EINVAL;
+    else if ( ref >= nr_grant_entries(gt) )
+        rc = -ENOENT;
+    else if ( gt->gt_version == 1 )
+    {
+        const grant_entry_v1_t *sha1 = &shared_entry_v1(gt, ref);
+
+        flags = sha1->flags;
+        *gfn = _gfn(sha1->frame);
+    }
+    else
+    {
+        const grant_entry_v2_t *sha2 = &shared_entry_v2(gt, ref);
+
+        flags = sha2->hdr.flags;
+        if ( flags & GTF_sub_page )
+           *gfn = _gfn(sha2->sub_page.frame);
+        else
+           *gfn = _gfn(sha2->full_page.frame);
+    }
+
+    if ( (flags & GTF_type_mask) != GTF_permit_access )
+        rc = -ENXIO;
+    else if ( !rc && status )
+    {
+        if ( gt->gt_version == 1 )
+            *status = flags;
+        else
+            *status = status_entry(gt, ref);
+    }
+
+    grant_read_unlock(gt);
+
+    return rc;
+}
+#endif
+
 static void gnttab_usage_print(struct domain *rd)
 {
     int first = 1;
--- a/xen/include/xen/grant_table.h
+++ b/xen/include/xen/grant_table.h
@@ -149,4 +149,7 @@ static inline unsigned int grant_to_stat
         GRANT_STATUS_PER_PAGE;
 }
 
+int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
+                            gfn_t *gfn, uint16_t *status);
+
 #endif /* __XEN_GRANT_TABLE_H__ */

Comments

Andrew Cooper Nov. 14, 2016, 11:56 a.m. UTC | #1
On 14/11/16 10:34, Jan Beulich wrote:
> --- a/xen/common/grant_table.c
> +++ b/xen/common/grant_table.c
> @@ -3438,6 +3438,53 @@ void grant_table_init_vcpu(struct vcpu *
>      v->maptrack_tail = MAPTRACK_TAIL;
>  }
>  
> +#ifdef CONFIG_HAS_MEM_SHARING
> +int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
> +                            gfn_t *gfn, uint16_t *status)
> +{
> +    int rc = 0;
> +    uint16_t flags = 0;
> +
> +    grant_read_lock(gt);
> +
> +    if ( gt->gt_version < 1 )
> +        rc = -EINVAL;
> +    else if ( ref >= nr_grant_entries(gt) )
> +        rc = -ENOENT;
> +    else if ( gt->gt_version == 1 )
> +    {
> +        const grant_entry_v1_t *sha1 = &shared_entry_v1(gt, ref);
> +
> +        flags = sha1->flags;
> +        *gfn = _gfn(sha1->frame);
> +    }
> +    else
> +    {
> +        const grant_entry_v2_t *sha2 = &shared_entry_v2(gt, ref);
> +
> +        flags = sha2->hdr.flags;
> +        if ( flags & GTF_sub_page )
> +           *gfn = _gfn(sha2->sub_page.frame);
> +        else
> +           *gfn = _gfn(sha2->full_page.frame);
> +    }
> +
> +    if ( (flags & GTF_type_mask) != GTF_permit_access )
> +        rc = -ENXIO;

This will clobber the EINVAL/ENOENT cases.  It wants to be pared with an
!rc &&.

With this fixed, Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>

> +    else if ( !rc && status )
> +    {
> +        if ( gt->gt_version == 1 )
> +            *status = flags;
> +        else
> +            *status = status_entry(gt, ref);
> +    }
> +
> +    grant_read_unlock(gt);
> +
> +    return rc;
> +}
> +#endif
> +
>  static void gnttab_usage_print(struct domain *rd)
>  {
>      int first = 1;
Jan Beulich Nov. 14, 2016, 12:56 p.m. UTC | #2
>>> On 14.11.16 at 12:56, <andrew.cooper3@citrix.com> wrote:
> On 14/11/16 10:34, Jan Beulich wrote:
>> --- a/xen/common/grant_table.c
>> +++ b/xen/common/grant_table.c
>> @@ -3438,6 +3438,53 @@ void grant_table_init_vcpu(struct vcpu *
>>      v->maptrack_tail = MAPTRACK_TAIL;
>>  }
>>  
>> +#ifdef CONFIG_HAS_MEM_SHARING
>> +int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
>> +                            gfn_t *gfn, uint16_t *status)
>> +{
>> +    int rc = 0;
>> +    uint16_t flags = 0;
>> +
>> +    grant_read_lock(gt);
>> +
>> +    if ( gt->gt_version < 1 )
>> +        rc = -EINVAL;
>> +    else if ( ref >= nr_grant_entries(gt) )
>> +        rc = -ENOENT;
>> +    else if ( gt->gt_version == 1 )
>> +    {
>> +        const grant_entry_v1_t *sha1 = &shared_entry_v1(gt, ref);
>> +
>> +        flags = sha1->flags;
>> +        *gfn = _gfn(sha1->frame);
>> +    }
>> +    else
>> +    {
>> +        const grant_entry_v2_t *sha2 = &shared_entry_v2(gt, ref);
>> +
>> +        flags = sha2->hdr.flags;
>> +        if ( flags & GTF_sub_page )
>> +           *gfn = _gfn(sha2->sub_page.frame);
>> +        else
>> +           *gfn = _gfn(sha2->full_page.frame);
>> +    }
>> +
>> +    if ( (flags & GTF_type_mask) != GTF_permit_access )
>> +        rc = -ENXIO;
> 
> This will clobber the EINVAL/ENOENT cases.  It wants to be pared with an
> !rc &&.

Oh, indeed - thanks for noticing.

> With this fixed, Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>

Thanks, Jan
Jan Beulich Nov. 22, 2016, 10:12 a.m. UTC | #3
>>> On 14.11.16 at 11:34, <JBeulich@suse.com> wrote:
> They need to be range checked against the current table limit in any
> event.
> 
> Reported-by: Huawei PSIRT <psirt@huawei.com>
> 
> Move the code to where it belongs, eliminating a number of duplicate
> definitions. Add locking. Produce proper error codes, and consume them
> instead of making one up. Check grant type. Convert parameter types at
> once.
> 
> Signed-off-by: Jan Beulich <jbeulich@suse.com>

Tamas? (The minor fix needed to address Andrew's reply doesn't seem
to warrant sending out a v2.)

> ---
> Note that likely there's more work needed subsequently: The grant isn't
> being marked in use for the duration of the use of the GFN. But I'll
> leave that to someone actually knowing how to properly to test this.
> 
> --- a/xen/arch/x86/mm/mem_sharing.c
> +++ b/xen/arch/x86/mm/mem_sharing.c
> @@ -753,75 +753,25 @@ int mem_sharing_debug_gfn(struct domain
>      return num_refs;
>  }
>  
> -#define SHGNT_PER_PAGE_V1 (PAGE_SIZE / sizeof(grant_entry_v1_t))
> -#define shared_entry_v1(t, e) \
> -    ((t)->shared_v1[(e)/SHGNT_PER_PAGE_V1][(e)%SHGNT_PER_PAGE_V1])
> -#define SHGNT_PER_PAGE_V2 (PAGE_SIZE / sizeof(grant_entry_v2_t))
> -#define shared_entry_v2(t, e) \
> -    ((t)->shared_v2[(e)/SHGNT_PER_PAGE_V2][(e)%SHGNT_PER_PAGE_V2])
> -#define STGNT_PER_PAGE (PAGE_SIZE / sizeof(grant_status_t))
> -#define status_entry(t, e) \
> -    ((t)->status[(e)/STGNT_PER_PAGE][(e)%STGNT_PER_PAGE])
> -
> -static grant_entry_header_t *
> -shared_entry_header(struct grant_table *t, grant_ref_t ref)
> -{
> -    ASSERT (t->gt_version != 0);
> -    if ( t->gt_version == 1 )
> -        return (grant_entry_header_t*)&shared_entry_v1(t, ref);
> -    else
> -        return &shared_entry_v2(t, ref).hdr;
> -}
> -
> -static int mem_sharing_gref_to_gfn(struct domain *d, 
> -                                   grant_ref_t ref, 
> -                                   unsigned long *gfn)
> -{
> -    if ( d->grant_table->gt_version < 1 )
> -        return -1;
> -
> -    if ( d->grant_table->gt_version == 1 ) 
> -    {
> -        grant_entry_v1_t *sha1;
> -        sha1 = &shared_entry_v1(d->grant_table, ref);
> -        *gfn = sha1->frame;
> -    } 
> -    else 
> -    {
> -        grant_entry_v2_t *sha2;
> -        sha2 = &shared_entry_v2(d->grant_table, ref);
> -        *gfn = sha2->full_page.frame;
> -    }
> - 
> -    return 0;
> -}
> -
> -
>  int mem_sharing_debug_gref(struct domain *d, grant_ref_t ref)
>  {
> -    grant_entry_header_t *shah;
> +    int rc;
>      uint16_t status;
> -    unsigned long gfn;
> +    gfn_t gfn;
>  
> -    if ( d->grant_table->gt_version < 1 )
> +    rc = mem_sharing_gref_to_gfn(d->grant_table, ref, &gfn, &status);
> +    if ( rc )
>      {
> -        MEM_SHARING_DEBUG( 
> -                "Asked to debug [dom=%d,gref=%d], but not yet inited.\n",
> -                d->domain_id, ref);
> -        return -EINVAL;
> -    }
> -    (void)mem_sharing_gref_to_gfn(d, ref, &gfn); 
> -    shah = shared_entry_header(d->grant_table, ref);
> -    if ( d->grant_table->gt_version == 1 ) 
> -        status = shah->flags;
> -    else 
> -        status = status_entry(d->grant_table, ref);
> +        MEM_SHARING_DEBUG("Asked to debug [dom=%d,gref=%u]: error %d.\n",
> +                          d->domain_id, ref, rc);
> +        return rc;
> +    }
>      
>      MEM_SHARING_DEBUG(
>              "==> Grant [dom=%d,ref=%d], status=%x. ", 
>              d->domain_id, ref, status);
>  
> -    return mem_sharing_debug_gfn(d, gfn); 
> +    return mem_sharing_debug_gfn(d, gfn_x(gfn));
>  }
>  
>  int mem_sharing_nominate_page(struct domain *d,
> @@ -1422,23 +1372,24 @@ int mem_sharing_memop(XEN_GUEST_HANDLE_P
>          case XENMEM_sharing_op_nominate_gref:
>          {
>              grant_ref_t gref = mso.u.nominate.u.grant_ref;
> -            unsigned long gfn;
> +            gfn_t gfn;
>              shr_handle_t handle;
>  
>              rc = -EINVAL;
>              if ( !mem_sharing_enabled(d) )
>                  goto out;
> -            if ( mem_sharing_gref_to_gfn(d, gref, &gfn) < 0 )
> +            rc = mem_sharing_gref_to_gfn(d->grant_table, gref, &gfn, NULL);
> +            if ( rc < 0 )
>                  goto out;
>  
> -            rc = mem_sharing_nominate_page(d, gfn, 3, &handle);
> +            rc = mem_sharing_nominate_page(d, gfn_x(gfn), 3, &handle);
>              mso.u.nominate.handle = handle;
>          }
>          break;
>  
>          case XENMEM_sharing_op_share:
>          {
> -            unsigned long sgfn, cgfn;
> +            gfn_t sgfn, cgfn;
>              struct domain *cd;
>              shr_handle_t sh, ch;
>  
> @@ -1470,35 +1421,38 @@ int mem_sharing_memop(XEN_GUEST_HANDLE_P
>                  grant_ref_t gref = (grant_ref_t) 
>                                      (XENMEM_SHARING_OP_FIELD_GET_GREF(
>                                          mso.u.share.source_gfn));
> -                if ( mem_sharing_gref_to_gfn(d, gref, &sgfn) < 0 )
> +                rc = mem_sharing_gref_to_gfn(d->grant_table, gref, &sgfn,
> +                                             NULL);
> +                if ( rc < 0 )
>                  {
>                      rcu_unlock_domain(cd);
> -                    rc = -EINVAL;
>                      goto out;
>                  }
> -            } else {
> -                sgfn  = mso.u.share.source_gfn;
>              }
> +            else
> +                sgfn = _gfn(mso.u.share.source_gfn);
>  
>              if ( XENMEM_SHARING_OP_FIELD_IS_GREF(mso.u.share.client_gfn) )
>              {
>                  grant_ref_t gref = (grant_ref_t) 
>                                      (XENMEM_SHARING_OP_FIELD_GET_GREF(
>                                          mso.u.share.client_gfn));
> -                if ( mem_sharing_gref_to_gfn(cd, gref, &cgfn) < 0 )
> +                rc = mem_sharing_gref_to_gfn(cd->grant_table, gref, &cgfn,
> +                                             NULL);
> +                if ( rc < 0 )
>                  {
>                      rcu_unlock_domain(cd);
> -                    rc = -EINVAL;
>                      goto out;
>                  }
> -            } else {
> -                cgfn  = mso.u.share.client_gfn;
>              }
> +            else
> +                cgfn = _gfn(mso.u.share.client_gfn);
>  
>              sh = mso.u.share.source_handle;
>              ch = mso.u.share.client_handle;
>  
> -            rc = mem_sharing_share_pages(d, sgfn, sh, cd, cgfn, ch); 
> +            rc = mem_sharing_share_pages(d, gfn_x(sgfn), sh,
> +                                         cd, gfn_x(cgfn), ch);
>  
>              rcu_unlock_domain(cd);
>          }
> --- a/xen/common/grant_table.c
> +++ b/xen/common/grant_table.c
> @@ -3438,6 +3438,53 @@ void grant_table_init_vcpu(struct vcpu *
>      v->maptrack_tail = MAPTRACK_TAIL;
>  }
>  
> +#ifdef CONFIG_HAS_MEM_SHARING
> +int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
> +                            gfn_t *gfn, uint16_t *status)
> +{
> +    int rc = 0;
> +    uint16_t flags = 0;
> +
> +    grant_read_lock(gt);
> +
> +    if ( gt->gt_version < 1 )
> +        rc = -EINVAL;
> +    else if ( ref >= nr_grant_entries(gt) )
> +        rc = -ENOENT;
> +    else if ( gt->gt_version == 1 )
> +    {
> +        const grant_entry_v1_t *sha1 = &shared_entry_v1(gt, ref);
> +
> +        flags = sha1->flags;
> +        *gfn = _gfn(sha1->frame);
> +    }
> +    else
> +    {
> +        const grant_entry_v2_t *sha2 = &shared_entry_v2(gt, ref);
> +
> +        flags = sha2->hdr.flags;
> +        if ( flags & GTF_sub_page )
> +           *gfn = _gfn(sha2->sub_page.frame);
> +        else
> +           *gfn = _gfn(sha2->full_page.frame);
> +    }
> +
> +    if ( (flags & GTF_type_mask) != GTF_permit_access )
> +        rc = -ENXIO;
> +    else if ( !rc && status )
> +    {
> +        if ( gt->gt_version == 1 )
> +            *status = flags;
> +        else
> +            *status = status_entry(gt, ref);
> +    }
> +
> +    grant_read_unlock(gt);
> +
> +    return rc;
> +}
> +#endif
> +
>  static void gnttab_usage_print(struct domain *rd)
>  {
>      int first = 1;
> --- a/xen/include/xen/grant_table.h
> +++ b/xen/include/xen/grant_table.h
> @@ -149,4 +149,7 @@ static inline unsigned int grant_to_stat
>          GRANT_STATUS_PER_PAGE;
>  }
>  
> +int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
> +                            gfn_t *gfn, uint16_t *status);
> +
>  #endif /* __XEN_GRANT_TABLE_H__ */
Tamas K Lengyel Nov. 22, 2016, 4:13 p.m. UTC | #4
On Tue, Nov 22, 2016 at 3:12 AM, Jan Beulich <JBeulich@suse.com> wrote:

> >>> On 14.11.16 at 11:34, <JBeulich@suse.com> wrote:
> > They need to be range checked against the current table limit in any
> > event.
> >
> > Reported-by: Huawei PSIRT <psirt@huawei.com>
> >
> > Move the code to where it belongs, eliminating a number of duplicate
> > definitions. Add locking. Produce proper error codes, and consume them
> > instead of making one up. Check grant type. Convert parameter types at
> > once.
> >
> > Signed-off-by: Jan Beulich <jbeulich@suse.com>
>
> Tamas? (The minor fix needed to address Andrew's reply doesn't seem
> to warrant sending out a v2.)


> > ---
> > Note that likely there's more work needed subsequently: The grant isn't
> > being marked in use for the duration of the use of the GFN. But I'll
> > leave that to someone actually knowing how to properly to test this.
>
>
Hi Jan,
unfortunately I don't have a good way to test this either as I never used
memsharing with grefs before. The above comment about the grant not being
marked for in-use makes me wonder whether this is a regression from this
patch or whether that just was never the case. Either way, I can see this
being an issue only if memory is being removed by hot-plugging, which AFAIK
is not a supported scenario anyway. The rest of the patch is fairly
mechanical, so:

Acked-by: Tamas K Lengyel <tamas@tklengyel.com>


> > --- a/xen/arch/x86/mm/mem_sharing.c
> > +++ b/xen/arch/x86/mm/mem_sharing.c
> > @@ -753,75 +753,25 @@ int mem_sharing_debug_gfn(struct domain
> >      return num_refs;
> >  }
> >
> > -#define SHGNT_PER_PAGE_V1 (PAGE_SIZE / sizeof(grant_entry_v1_t))
> > -#define shared_entry_v1(t, e) \
> > -    ((t)->shared_v1[(e)/SHGNT_PER_PAGE_V1][(e)%SHGNT_PER_PAGE_V1])
> > -#define SHGNT_PER_PAGE_V2 (PAGE_SIZE / sizeof(grant_entry_v2_t))
> > -#define shared_entry_v2(t, e) \
> > -    ((t)->shared_v2[(e)/SHGNT_PER_PAGE_V2][(e)%SHGNT_PER_PAGE_V2])
> > -#define STGNT_PER_PAGE (PAGE_SIZE / sizeof(grant_status_t))
> > -#define status_entry(t, e) \
> > -    ((t)->status[(e)/STGNT_PER_PAGE][(e)%STGNT_PER_PAGE])
> > -
> > -static grant_entry_header_t *
> > -shared_entry_header(struct grant_table *t, grant_ref_t ref)
> > -{
> > -    ASSERT (t->gt_version != 0);
> > -    if ( t->gt_version == 1 )
> > -        return (grant_entry_header_t*)&shared_entry_v1(t, ref);
> > -    else
> > -        return &shared_entry_v2(t, ref).hdr;
> > -}
> > -
> > -static int mem_sharing_gref_to_gfn(struct domain *d,
> > -                                   grant_ref_t ref,
> > -                                   unsigned long *gfn)
> > -{
> > -    if ( d->grant_table->gt_version < 1 )
> > -        return -1;
> > -
> > -    if ( d->grant_table->gt_version == 1 )
> > -    {
> > -        grant_entry_v1_t *sha1;
> > -        sha1 = &shared_entry_v1(d->grant_table, ref);
> > -        *gfn = sha1->frame;
> > -    }
> > -    else
> > -    {
> > -        grant_entry_v2_t *sha2;
> > -        sha2 = &shared_entry_v2(d->grant_table, ref);
> > -        *gfn = sha2->full_page.frame;
> > -    }
> > -
> > -    return 0;
> > -}
> > -
> > -
> >  int mem_sharing_debug_gref(struct domain *d, grant_ref_t ref)
> >  {
> > -    grant_entry_header_t *shah;
> > +    int rc;
> >      uint16_t status;
> > -    unsigned long gfn;
> > +    gfn_t gfn;
> >
> > -    if ( d->grant_table->gt_version < 1 )
> > +    rc = mem_sharing_gref_to_gfn(d->grant_table, ref, &gfn, &status);
> > +    if ( rc )
> >      {
> > -        MEM_SHARING_DEBUG(
> > -                "Asked to debug [dom=%d,gref=%d], but not yet
> inited.\n",
> > -                d->domain_id, ref);
> > -        return -EINVAL;
> > -    }
> > -    (void)mem_sharing_gref_to_gfn(d, ref, &gfn);
> > -    shah = shared_entry_header(d->grant_table, ref);
> > -    if ( d->grant_table->gt_version == 1 )
> > -        status = shah->flags;
> > -    else
> > -        status = status_entry(d->grant_table, ref);
> > +        MEM_SHARING_DEBUG("Asked to debug [dom=%d,gref=%u]: error
> %d.\n",
> > +                          d->domain_id, ref, rc);
> > +        return rc;
> > +    }
> >
> >      MEM_SHARING_DEBUG(
> >              "==> Grant [dom=%d,ref=%d], status=%x. ",
> >              d->domain_id, ref, status);
> >
> > -    return mem_sharing_debug_gfn(d, gfn);
> > +    return mem_sharing_debug_gfn(d, gfn_x(gfn));
> >  }
> >
> >  int mem_sharing_nominate_page(struct domain *d,
> > @@ -1422,23 +1372,24 @@ int mem_sharing_memop(XEN_GUEST_HANDLE_P
> >          case XENMEM_sharing_op_nominate_gref:
> >          {
> >              grant_ref_t gref = mso.u.nominate.u.grant_ref;
> > -            unsigned long gfn;
> > +            gfn_t gfn;
> >              shr_handle_t handle;
> >
> >              rc = -EINVAL;
> >              if ( !mem_sharing_enabled(d) )
> >                  goto out;
> > -            if ( mem_sharing_gref_to_gfn(d, gref, &gfn) < 0 )
> > +            rc = mem_sharing_gref_to_gfn(d->grant_table, gref, &gfn,
> NULL);
> > +            if ( rc < 0 )
> >                  goto out;
> >
> > -            rc = mem_sharing_nominate_page(d, gfn, 3, &handle);
> > +            rc = mem_sharing_nominate_page(d, gfn_x(gfn), 3, &handle);
> >              mso.u.nominate.handle = handle;
> >          }
> >          break;
> >
> >          case XENMEM_sharing_op_share:
> >          {
> > -            unsigned long sgfn, cgfn;
> > +            gfn_t sgfn, cgfn;
> >              struct domain *cd;
> >              shr_handle_t sh, ch;
> >
> > @@ -1470,35 +1421,38 @@ int mem_sharing_memop(XEN_GUEST_HANDLE_P
> >                  grant_ref_t gref = (grant_ref_t)
> >                                      (XENMEM_SHARING_OP_FIELD_GET_GREF(
> >                                          mso.u.share.source_gfn));
> > -                if ( mem_sharing_gref_to_gfn(d, gref, &sgfn) < 0 )
> > +                rc = mem_sharing_gref_to_gfn(d->grant_table, gref,
> &sgfn,
> > +                                             NULL);
> > +                if ( rc < 0 )
> >                  {
> >                      rcu_unlock_domain(cd);
> > -                    rc = -EINVAL;
> >                      goto out;
> >                  }
> > -            } else {
> > -                sgfn  = mso.u.share.source_gfn;
> >              }
> > +            else
> > +                sgfn = _gfn(mso.u.share.source_gfn);
> >
> >              if ( XENMEM_SHARING_OP_FIELD_IS_GREF(mso.u.share.client_gfn)
> )
> >              {
> >                  grant_ref_t gref = (grant_ref_t)
> >                                      (XENMEM_SHARING_OP_FIELD_GET_GREF(
> >                                          mso.u.share.client_gfn));
> > -                if ( mem_sharing_gref_to_gfn(cd, gref, &cgfn) < 0 )
> > +                rc = mem_sharing_gref_to_gfn(cd->grant_table, gref,
> &cgfn,
> > +                                             NULL);
> > +                if ( rc < 0 )
> >                  {
> >                      rcu_unlock_domain(cd);
> > -                    rc = -EINVAL;
> >                      goto out;
> >                  }
> > -            } else {
> > -                cgfn  = mso.u.share.client_gfn;
> >              }
> > +            else
> > +                cgfn = _gfn(mso.u.share.client_gfn);
> >
> >              sh = mso.u.share.source_handle;
> >              ch = mso.u.share.client_handle;
> >
> > -            rc = mem_sharing_share_pages(d, sgfn, sh, cd, cgfn, ch);
> > +            rc = mem_sharing_share_pages(d, gfn_x(sgfn), sh,
> > +                                         cd, gfn_x(cgfn), ch);
> >
> >              rcu_unlock_domain(cd);
> >          }
> > --- a/xen/common/grant_table.c
> > +++ b/xen/common/grant_table.c
> > @@ -3438,6 +3438,53 @@ void grant_table_init_vcpu(struct vcpu *
> >      v->maptrack_tail = MAPTRACK_TAIL;
> >  }
> >
> > +#ifdef CONFIG_HAS_MEM_SHARING
> > +int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
> > +                            gfn_t *gfn, uint16_t *status)
> > +{
> > +    int rc = 0;
> > +    uint16_t flags = 0;
> > +
> > +    grant_read_lock(gt);
> > +
> > +    if ( gt->gt_version < 1 )
> > +        rc = -EINVAL;
> > +    else if ( ref >= nr_grant_entries(gt) )
> > +        rc = -ENOENT;
> > +    else if ( gt->gt_version == 1 )
> > +    {
> > +        const grant_entry_v1_t *sha1 = &shared_entry_v1(gt, ref);
> > +
> > +        flags = sha1->flags;
> > +        *gfn = _gfn(sha1->frame);
> > +    }
> > +    else
> > +    {
> > +        const grant_entry_v2_t *sha2 = &shared_entry_v2(gt, ref);
> > +
> > +        flags = sha2->hdr.flags;
> > +        if ( flags & GTF_sub_page )
> > +           *gfn = _gfn(sha2->sub_page.frame);
> > +        else
> > +           *gfn = _gfn(sha2->full_page.frame);
> > +    }
> > +
> > +    if ( (flags & GTF_type_mask) != GTF_permit_access )
> > +        rc = -ENXIO;
> > +    else if ( !rc && status )
> > +    {
> > +        if ( gt->gt_version == 1 )
> > +            *status = flags;
> > +        else
> > +            *status = status_entry(gt, ref);
> > +    }
> > +
> > +    grant_read_unlock(gt);
> > +
> > +    return rc;
> > +}
> > +#endif
> > +
> >  static void gnttab_usage_print(struct domain *rd)
> >  {
> >      int first = 1;
> > --- a/xen/include/xen/grant_table.h
> > +++ b/xen/include/xen/grant_table.h
> > @@ -149,4 +149,7 @@ static inline unsigned int grant_to_stat
> >          GRANT_STATUS_PER_PAGE;
> >  }
> >
> > +int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
> > +                            gfn_t *gfn, uint16_t *status);
> > +
> >  #endif /* __XEN_GRANT_TABLE_H__ */
>
>
Jan Beulich Nov. 22, 2016, 4:17 p.m. UTC | #5
>>> On 22.11.16 at 17:13, <tamas@tklengyel.com> wrote:
> On Tue, Nov 22, 2016 at 3:12 AM, Jan Beulich <JBeulich@suse.com> wrote:
> 
>> >>> On 14.11.16 at 11:34, <JBeulich@suse.com> wrote:
>> > They need to be range checked against the current table limit in any
>> > event.
>> >
>> > Reported-by: Huawei PSIRT <psirt@huawei.com>
>> >
>> > Move the code to where it belongs, eliminating a number of duplicate
>> > definitions. Add locking. Produce proper error codes, and consume them
>> > instead of making one up. Check grant type. Convert parameter types at
>> > once.
>> >
>> > Signed-off-by: Jan Beulich <jbeulich@suse.com>
>>
>> Tamas? (The minor fix needed to address Andrew's reply doesn't seem
>> to warrant sending out a v2.)
> 
> 
>> > ---
>> > Note that likely there's more work needed subsequently: The grant isn't
>> > being marked in use for the duration of the use of the GFN. But I'll
>> > leave that to someone actually knowing how to properly to test this.
>>
>>
> Hi Jan,
> unfortunately I don't have a good way to test this either as I never used
> memsharing with grefs before. The above comment about the grant not being
> marked for in-use makes me wonder whether this is a regression from this
> patch or whether that just was never the case.

This was never the case. I wouldn't dare to submit a patch
knowingly breaking something.

> Either way, I can see this
> being an issue only if memory is being removed by hot-plugging, which AFAIK
> is not a supported scenario anyway. The rest of the patch is fairly
> mechanical, so:
> 
> Acked-by: Tamas K Lengyel <tamas@tklengyel.com>

Thanks.

Jan
Wei Liu Nov. 22, 2016, 4:22 p.m. UTC | #6
On Tue, Nov 22, 2016 at 09:17:43AM -0700, Jan Beulich wrote:
> >>> On 22.11.16 at 17:13, <tamas@tklengyel.com> wrote:
> > On Tue, Nov 22, 2016 at 3:12 AM, Jan Beulich <JBeulich@suse.com> wrote:
> > 
> >> >>> On 14.11.16 at 11:34, <JBeulich@suse.com> wrote:
> >> > They need to be range checked against the current table limit in any
> >> > event.
> >> >
> >> > Reported-by: Huawei PSIRT <psirt@huawei.com>
> >> >
> >> > Move the code to where it belongs, eliminating a number of duplicate
> >> > definitions. Add locking. Produce proper error codes, and consume them
> >> > instead of making one up. Check grant type. Convert parameter types at
> >> > once.
> >> >
> >> > Signed-off-by: Jan Beulich <jbeulich@suse.com>
> >>
> >> Tamas? (The minor fix needed to address Andrew's reply doesn't seem
> >> to warrant sending out a v2.)
> > 
> > 
> >> > ---
> >> > Note that likely there's more work needed subsequently: The grant isn't
> >> > being marked in use for the duration of the use of the GFN. But I'll
> >> > leave that to someone actually knowing how to properly to test this.
> >>
> >>
> > Hi Jan,
> > unfortunately I don't have a good way to test this either as I never used
> > memsharing with grefs before. The above comment about the grant not being
> > marked for in-use makes me wonder whether this is a regression from this
> > patch or whether that just was never the case.
> 
> This was never the case. I wouldn't dare to submit a patch
> knowingly breaking something.
> 
> > Either way, I can see this
> > being an issue only if memory is being removed by hot-plugging, which AFAIK
> > is not a supported scenario anyway. The rest of the patch is fairly
> > mechanical, so:
> > 
> > Acked-by: Tamas K Lengyel <tamas@tklengyel.com>

Release-acked-by: Wei Liu <wei.liu2@citrix.com>
diff mbox

Patch

--- a/xen/arch/x86/mm/mem_sharing.c
+++ b/xen/arch/x86/mm/mem_sharing.c
@@ -753,75 +753,25 @@  int mem_sharing_debug_gfn(struct domain
     return num_refs;
 }
 
-#define SHGNT_PER_PAGE_V1 (PAGE_SIZE / sizeof(grant_entry_v1_t))
-#define shared_entry_v1(t, e) \
-    ((t)->shared_v1[(e)/SHGNT_PER_PAGE_V1][(e)%SHGNT_PER_PAGE_V1])
-#define SHGNT_PER_PAGE_V2 (PAGE_SIZE / sizeof(grant_entry_v2_t))
-#define shared_entry_v2(t, e) \
-    ((t)->shared_v2[(e)/SHGNT_PER_PAGE_V2][(e)%SHGNT_PER_PAGE_V2])
-#define STGNT_PER_PAGE (PAGE_SIZE / sizeof(grant_status_t))
-#define status_entry(t, e) \
-    ((t)->status[(e)/STGNT_PER_PAGE][(e)%STGNT_PER_PAGE])
-
-static grant_entry_header_t *
-shared_entry_header(struct grant_table *t, grant_ref_t ref)
-{
-    ASSERT (t->gt_version != 0);
-    if ( t->gt_version == 1 )
-        return (grant_entry_header_t*)&shared_entry_v1(t, ref);
-    else
-        return &shared_entry_v2(t, ref).hdr;
-}
-
-static int mem_sharing_gref_to_gfn(struct domain *d, 
-                                   grant_ref_t ref, 
-                                   unsigned long *gfn)
-{
-    if ( d->grant_table->gt_version < 1 )
-        return -1;
-
-    if ( d->grant_table->gt_version == 1 ) 
-    {
-        grant_entry_v1_t *sha1;
-        sha1 = &shared_entry_v1(d->grant_table, ref);
-        *gfn = sha1->frame;
-    } 
-    else 
-    {
-        grant_entry_v2_t *sha2;
-        sha2 = &shared_entry_v2(d->grant_table, ref);
-        *gfn = sha2->full_page.frame;
-    }
- 
-    return 0;
-}
-
-
 int mem_sharing_debug_gref(struct domain *d, grant_ref_t ref)
 {
-    grant_entry_header_t *shah;
+    int rc;
     uint16_t status;
-    unsigned long gfn;
+    gfn_t gfn;
 
-    if ( d->grant_table->gt_version < 1 )
+    rc = mem_sharing_gref_to_gfn(d->grant_table, ref, &gfn, &status);
+    if ( rc )
     {
-        MEM_SHARING_DEBUG( 
-                "Asked to debug [dom=%d,gref=%d], but not yet inited.\n",
-                d->domain_id, ref);
-        return -EINVAL;
-    }
-    (void)mem_sharing_gref_to_gfn(d, ref, &gfn); 
-    shah = shared_entry_header(d->grant_table, ref);
-    if ( d->grant_table->gt_version == 1 ) 
-        status = shah->flags;
-    else 
-        status = status_entry(d->grant_table, ref);
+        MEM_SHARING_DEBUG("Asked to debug [dom=%d,gref=%u]: error %d.\n",
+                          d->domain_id, ref, rc);
+        return rc;
+    }
     
     MEM_SHARING_DEBUG(
             "==> Grant [dom=%d,ref=%d], status=%x. ", 
             d->domain_id, ref, status);
 
-    return mem_sharing_debug_gfn(d, gfn); 
+    return mem_sharing_debug_gfn(d, gfn_x(gfn));
 }
 
 int mem_sharing_nominate_page(struct domain *d,
@@ -1422,23 +1372,24 @@  int mem_sharing_memop(XEN_GUEST_HANDLE_P
         case XENMEM_sharing_op_nominate_gref:
         {
             grant_ref_t gref = mso.u.nominate.u.grant_ref;
-            unsigned long gfn;
+            gfn_t gfn;
             shr_handle_t handle;
 
             rc = -EINVAL;
             if ( !mem_sharing_enabled(d) )
                 goto out;
-            if ( mem_sharing_gref_to_gfn(d, gref, &gfn) < 0 )
+            rc = mem_sharing_gref_to_gfn(d->grant_table, gref, &gfn, NULL);
+            if ( rc < 0 )
                 goto out;
 
-            rc = mem_sharing_nominate_page(d, gfn, 3, &handle);
+            rc = mem_sharing_nominate_page(d, gfn_x(gfn), 3, &handle);
             mso.u.nominate.handle = handle;
         }
         break;
 
         case XENMEM_sharing_op_share:
         {
-            unsigned long sgfn, cgfn;
+            gfn_t sgfn, cgfn;
             struct domain *cd;
             shr_handle_t sh, ch;
 
@@ -1470,35 +1421,38 @@  int mem_sharing_memop(XEN_GUEST_HANDLE_P
                 grant_ref_t gref = (grant_ref_t) 
                                     (XENMEM_SHARING_OP_FIELD_GET_GREF(
                                         mso.u.share.source_gfn));
-                if ( mem_sharing_gref_to_gfn(d, gref, &sgfn) < 0 )
+                rc = mem_sharing_gref_to_gfn(d->grant_table, gref, &sgfn,
+                                             NULL);
+                if ( rc < 0 )
                 {
                     rcu_unlock_domain(cd);
-                    rc = -EINVAL;
                     goto out;
                 }
-            } else {
-                sgfn  = mso.u.share.source_gfn;
             }
+            else
+                sgfn = _gfn(mso.u.share.source_gfn);
 
             if ( XENMEM_SHARING_OP_FIELD_IS_GREF(mso.u.share.client_gfn) )
             {
                 grant_ref_t gref = (grant_ref_t) 
                                     (XENMEM_SHARING_OP_FIELD_GET_GREF(
                                         mso.u.share.client_gfn));
-                if ( mem_sharing_gref_to_gfn(cd, gref, &cgfn) < 0 )
+                rc = mem_sharing_gref_to_gfn(cd->grant_table, gref, &cgfn,
+                                             NULL);
+                if ( rc < 0 )
                 {
                     rcu_unlock_domain(cd);
-                    rc = -EINVAL;
                     goto out;
                 }
-            } else {
-                cgfn  = mso.u.share.client_gfn;
             }
+            else
+                cgfn = _gfn(mso.u.share.client_gfn);
 
             sh = mso.u.share.source_handle;
             ch = mso.u.share.client_handle;
 
-            rc = mem_sharing_share_pages(d, sgfn, sh, cd, cgfn, ch); 
+            rc = mem_sharing_share_pages(d, gfn_x(sgfn), sh,
+                                         cd, gfn_x(cgfn), ch);
 
             rcu_unlock_domain(cd);
         }
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -3438,6 +3438,53 @@  void grant_table_init_vcpu(struct vcpu *
     v->maptrack_tail = MAPTRACK_TAIL;
 }
 
+#ifdef CONFIG_HAS_MEM_SHARING
+int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
+                            gfn_t *gfn, uint16_t *status)
+{
+    int rc = 0;
+    uint16_t flags = 0;
+
+    grant_read_lock(gt);
+
+    if ( gt->gt_version < 1 )
+        rc = -EINVAL;
+    else if ( ref >= nr_grant_entries(gt) )
+        rc = -ENOENT;
+    else if ( gt->gt_version == 1 )
+    {
+        const grant_entry_v1_t *sha1 = &shared_entry_v1(gt, ref);
+
+        flags = sha1->flags;
+        *gfn = _gfn(sha1->frame);
+    }
+    else
+    {
+        const grant_entry_v2_t *sha2 = &shared_entry_v2(gt, ref);
+
+        flags = sha2->hdr.flags;
+        if ( flags & GTF_sub_page )
+           *gfn = _gfn(sha2->sub_page.frame);
+        else
+           *gfn = _gfn(sha2->full_page.frame);
+    }
+
+    if ( (flags & GTF_type_mask) != GTF_permit_access )
+        rc = -ENXIO;
+    else if ( !rc && status )
+    {
+        if ( gt->gt_version == 1 )
+            *status = flags;
+        else
+            *status = status_entry(gt, ref);
+    }
+
+    grant_read_unlock(gt);
+
+    return rc;
+}
+#endif
+
 static void gnttab_usage_print(struct domain *rd)
 {
     int first = 1;
--- a/xen/include/xen/grant_table.h
+++ b/xen/include/xen/grant_table.h
@@ -149,4 +149,7 @@  static inline unsigned int grant_to_stat
         GRANT_STATUS_PER_PAGE;
 }
 
+int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref,
+                            gfn_t *gfn, uint16_t *status);
+
 #endif /* __XEN_GRANT_TABLE_H__ */