debuggers.hg

changeset 22240:e8e3aeed3eba

tmem: disallow bad gmfns from PV domains

Mfns for PV domains were not properly checked, potentially
allowing a buggy or malicious PV guest to crash Xen. Also,
use get_page/put_page to claim a reference to the pages
so they can't disappear out from under tmem's feet.

Signed-off-by: Dan Magenheimer <dan.magenheimer@oracle.com>
author Keir Fraser <keir.fraser@citrix.com>
date Wed Sep 22 08:54:08 2010 +0100 (2010-09-22)
parents 35a1a14c408e
children 68cd8ae1b620
files xen/common/tmem_xen.c
line diff
     1.1 --- a/xen/common/tmem_xen.c	Wed Sep 22 08:49:09 2010 +0100
     1.2 +++ b/xen/common/tmem_xen.c	Wed Sep 22 08:54:08 2010 +0100
     1.3 @@ -88,49 +88,88 @@ void tmh_copy_page(char *to, char*from)
     1.4  }
     1.5  
     1.6  #ifdef __ia64__
     1.7 -static inline void *cli_mfn_to_va(tmem_cli_mfn_t cmfn, unsigned long *pcli_mfn)
     1.8 +static inline void *cli_get_page(tmem_cli_mfn_t cmfn, unsigned long *pcli_mfn,
     1.9 +                                 pfp_t **pcli_pfp, bool_t cli_write)
    1.10  {
    1.11      ASSERT(0);
    1.12      return NULL;
    1.13  }
    1.14 -#define paging_mark_dirty(_x,_y) do {} while(0)
    1.15 +
    1.16 +static inline void cli_put_page(void *cli_va, struct page_info *cli_pfp,
    1.17 +                                bool_t mark_dirty)
    1.18 +{
    1.19 +    ASSERT(0);
    1.20 +}
    1.21  #else
    1.22 -static inline void *cli_mfn_to_va(tmem_cli_mfn_t cmfn, unsigned long *pcli_mfn)
    1.23 +static inline void *cli_get_page(tmem_cli_mfn_t cmfn, unsigned long *pcli_mfn,
    1.24 +                                 pfp_t **pcli_pfp, bool_t cli_write)
    1.25  {
    1.26      unsigned long cli_mfn;
    1.27      p2m_type_t t;
    1.28 +    struct page_info *page;
    1.29 +    int ret;
    1.30  
    1.31      cli_mfn = mfn_x(gfn_to_mfn(p2m_get_hostp2m(current->domain), cmfn, &t));
    1.32 -    if (t != p2m_ram_rw || cli_mfn == INVALID_MFN)
    1.33 +    if ( t != p2m_ram_rw || !mfn_valid(cli_mfn) )
    1.34 +            return NULL;
    1.35 +    page = mfn_to_page(cli_mfn);
    1.36 +    if ( cli_write )
    1.37 +        ret = get_page_and_type(page, current->domain, PGT_writable_page);
    1.38 +    else
    1.39 +        ret = get_page(page, current->domain);
    1.40 +    if ( !ret )
    1.41          return NULL;
    1.42 -    if (pcli_mfn != NULL)
    1.43 -        *pcli_mfn = cli_mfn;
    1.44 +    *pcli_mfn = cli_mfn;
    1.45 +    *pcli_pfp = (pfp_t *)page;
    1.46      return map_domain_page(cli_mfn);
    1.47  }
    1.48 +
    1.49 +static inline void cli_put_page(void *cli_va, pfp_t *cli_pfp,
    1.50 +                                unsigned long cli_mfn, bool_t mark_dirty)
    1.51 +{
    1.52 +    if ( mark_dirty )
    1.53 +    {
    1.54 +        put_page_and_type((struct page_info *)cli_pfp);
    1.55 +        paging_mark_dirty(current->domain,cli_mfn);
    1.56 +    }
    1.57 +    else
    1.58 +        put_page((struct page_info *)cli_pfp);
    1.59 +    unmap_domain_page(cli_va);
    1.60 +}
    1.61  #endif
    1.62  
    1.63  EXPORT int tmh_copy_from_client(pfp_t *pfp,
    1.64      tmem_cli_mfn_t cmfn, pagesize_t tmem_offset,
    1.65      pagesize_t pfn_offset, pagesize_t len, void *cli_va)
    1.66  {
    1.67 -    unsigned long tmem_mfn;
    1.68 +    unsigned long tmem_mfn, cli_mfn = 0;
    1.69      void *tmem_va;
    1.70 +    pfp_t *cli_pfp = NULL;
    1.71 +    bool_t tmemc = cli_va != NULL; /* if true, cli_va is control-op buffer */
    1.72  
    1.73      ASSERT(pfp != NULL);
    1.74 -    if ( tmem_offset || pfn_offset || len )
    1.75 -        if ( (cli_va == NULL) && ((cli_va = cli_mfn_to_va(cmfn,NULL)) == NULL) )
    1.76 -            return -EFAULT;
    1.77      tmem_mfn = page_to_mfn(pfp);
    1.78      tmem_va = map_domain_page(tmem_mfn);
    1.79 +    if ( tmem_offset == 0 && pfn_offset == 0 && len == 0 )
    1.80 +    {
    1.81 +        memset(tmem_va, 0, PAGE_SIZE);
    1.82 +        unmap_domain_page(tmem_va);
    1.83 +        return 1;
    1.84 +    }
    1.85 +    if ( !tmemc )
    1.86 +    {
    1.87 +        cli_va = cli_get_page(cmfn, &cli_mfn, &cli_pfp, 0);
    1.88 +        if ( cli_va == NULL )
    1.89 +            return -EFAULT;
    1.90 +    }
    1.91      mb();
    1.92 -    if (!len && !tmem_offset && !pfn_offset)
    1.93 -        memset(tmem_va, 0, PAGE_SIZE);
    1.94 -    else if (len == PAGE_SIZE && !tmem_offset && !pfn_offset)
    1.95 +    if (len == PAGE_SIZE && !tmem_offset && !pfn_offset)
    1.96          tmh_copy_page(tmem_va, cli_va);
    1.97      else if ( (tmem_offset+len <= PAGE_SIZE) &&
    1.98 -                (pfn_offset+len <= PAGE_SIZE) ) 
    1.99 +              (pfn_offset+len <= PAGE_SIZE) )
   1.100          memcpy((char *)tmem_va+tmem_offset,(char *)cli_va+pfn_offset,len);
   1.101 -    unmap_domain_page(cli_va);
   1.102 +    if ( !tmemc )
   1.103 +        cli_put_page(cli_va, cli_pfp, cli_mfn, 0);
   1.104      unmap_domain_page(tmem_va);
   1.105      return 1;
   1.106  }
   1.107 @@ -141,15 +180,24 @@ EXPORT int tmh_compress_from_client(tmem
   1.108      int ret = 0;
   1.109      unsigned char *dmem = this_cpu(dstmem);
   1.110      unsigned char *wmem = this_cpu(workmem);
   1.111 +    pfp_t *cli_pfp = NULL;
   1.112 +    unsigned long cli_mfn = 0;
   1.113 +    bool_t tmemc = cli_va != NULL; /* if true, cli_va is control-op buffer */
   1.114  
   1.115 -    if ( (cli_va == NULL) && (cli_va = cli_mfn_to_va(cmfn,NULL)) == NULL)
   1.116 -        return -EFAULT;
   1.117      if ( dmem == NULL || wmem == NULL )
   1.118          return 0;  /* no buffer, so can't compress */
   1.119 +    if ( !tmemc )
   1.120 +    {
   1.121 +        cli_va = cli_get_page(cmfn, &cli_mfn, &cli_pfp, 0);
   1.122 +        if ( cli_va == NULL )
   1.123 +            return -EFAULT;
   1.124 +    }
   1.125      mb();
   1.126      ret = lzo1x_1_compress(cli_va, PAGE_SIZE, dmem, out_len, wmem);
   1.127      ASSERT(ret == LZO_E_OK);
   1.128      *out_va = dmem;
   1.129 +    if ( !tmemc )
   1.130 +        cli_put_page(cli_va, cli_pfp, cli_mfn, 0);
   1.131      unmap_domain_page(cli_va);
   1.132      return 1;
   1.133  }
   1.134 @@ -158,14 +206,17 @@ EXPORT int tmh_copy_to_client(tmem_cli_m
   1.135      pagesize_t tmem_offset, pagesize_t pfn_offset, pagesize_t len, void *cli_va)
   1.136  {
   1.137      unsigned long tmem_mfn, cli_mfn = 0;
   1.138 -    int mark_dirty = 1;
   1.139      void *tmem_va;
   1.140 +    pfp_t *cli_pfp = NULL;
   1.141 +    bool_t tmemc = cli_va != NULL; /* if true, cli_va is control-op buffer */
   1.142  
   1.143      ASSERT(pfp != NULL);
   1.144 -    if ( cli_va != NULL )
   1.145 -        mark_dirty = 0;
   1.146 -    else if ( (cli_va = cli_mfn_to_va(cmfn,&cli_mfn)) == NULL)
   1.147 -        return -EFAULT;
   1.148 +    if ( !tmemc )
   1.149 +    {
   1.150 +        cli_va = cli_get_page(cmfn, &cli_mfn, &cli_pfp, 1);
   1.151 +        if ( cli_va == NULL )
   1.152 +            return -EFAULT;
   1.153 +    }
   1.154      tmem_mfn = page_to_mfn(pfp);
   1.155      tmem_va = map_domain_page(tmem_mfn);
   1.156      if (len == PAGE_SIZE && !tmem_offset && !pfn_offset)
   1.157 @@ -173,11 +224,8 @@ EXPORT int tmh_copy_to_client(tmem_cli_m
   1.158      else if ( (tmem_offset+len <= PAGE_SIZE) && (pfn_offset+len <= PAGE_SIZE) )
   1.159          memcpy((char *)cli_va+pfn_offset,(char *)tmem_va+tmem_offset,len);
   1.160      unmap_domain_page(tmem_va);
   1.161 -    if ( mark_dirty )
   1.162 -    {
   1.163 -        unmap_domain_page(cli_va);
   1.164 -        paging_mark_dirty(current->domain,cli_mfn);
   1.165 -    }
   1.166 +    if ( !tmemc )
   1.167 +        cli_put_page(cli_va, cli_pfp, cli_mfn, 1);
   1.168      mb();
   1.169      return 1;
   1.170  }
   1.171 @@ -186,22 +234,22 @@ EXPORT int tmh_decompress_to_client(tmem
   1.172                                      size_t size, void *cli_va)
   1.173  {
   1.174      unsigned long cli_mfn = 0;
   1.175 -    int mark_dirty = 1;
   1.176 +    pfp_t *cli_pfp = NULL;
   1.177      size_t out_len = PAGE_SIZE;
   1.178 +    bool_t tmemc = cli_va != NULL; /* if true, cli_va is control-op buffer */
   1.179      int ret;
   1.180  
   1.181 -    if ( cli_va != NULL )
   1.182 -        mark_dirty = 0;
   1.183 -    else if ( (cli_va = cli_mfn_to_va(cmfn,&cli_mfn)) == NULL)
   1.184 -        return -EFAULT;
   1.185 +    if ( !tmemc )
   1.186 +    {
   1.187 +        cli_va = cli_get_page(cmfn, &cli_mfn, &cli_pfp, 1);
   1.188 +        if ( cli_va == NULL )
   1.189 +            return -EFAULT;
   1.190 +    }
   1.191      ret = lzo1x_decompress_safe(tmem_va, size, cli_va, &out_len);
   1.192      ASSERT(ret == LZO_E_OK);
   1.193      ASSERT(out_len == PAGE_SIZE);
   1.194 -    if ( mark_dirty )
   1.195 -    {
   1.196 -        unmap_domain_page(cli_va);
   1.197 -        paging_mark_dirty(current->domain,cli_mfn);
   1.198 -    }
   1.199 +    if ( !tmemc )
   1.200 +        cli_put_page(cli_va, cli_pfp, cli_mfn, 1);
   1.201      mb();
   1.202      return 1;
   1.203  }
   1.204 @@ -211,18 +259,19 @@ EXPORT int tmh_copy_tze_to_client(tmem_c
   1.205  {
   1.206      void *cli_va;
   1.207      unsigned long cli_mfn;
   1.208 +    pfp_t *cli_pfp = NULL;
   1.209  
   1.210      ASSERT(!(len & (sizeof(uint64_t)-1)));
   1.211      ASSERT(len <= PAGE_SIZE);
   1.212      ASSERT(len > 0 || tmem_va == NULL);
   1.213 -    if ( (cli_va = cli_mfn_to_va(cmfn,&cli_mfn)) == NULL)
   1.214 +    cli_va = cli_get_page(cmfn, &cli_mfn, &cli_pfp, 1);
   1.215 +    if ( cli_va == NULL )
   1.216          return -EFAULT;
   1.217      if ( len > 0 )
   1.218          memcpy((char *)cli_va,(char *)tmem_va,len);
   1.219      if ( len < PAGE_SIZE )
   1.220          memset((char *)cli_va+len,0,PAGE_SIZE-len);
   1.221 -    unmap_domain_page(cli_va);
   1.222 -    paging_mark_dirty(current->domain,cli_mfn);
   1.223 +    cli_put_page(cli_va, cli_pfp, cli_mfn, 1);
   1.224      mb();
   1.225      return 1;
   1.226  }