[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v1 1/2] xc_core_arch_map_p2m_tree_rw: fix memory leak




> On 24 Feb 2023, at 14:56, Andrew Cooper <Andrew.Cooper3@xxxxxxxxxx> wrote:
> 
> On 24/02/2023 1:36 pm, Edwin Török wrote:
>> From: Edwin Török <edwin.torok@xxxxxxxxx>
>> 
>> Prior to bd7a29c3d0 'out' would've always been executed and memory
>> freed, but that commit changed it such that it returns early and leaks.
>> 
>> Found using gcc 12.2.1 `-fanalyzer`:
>> ```
>> xg_core_x86.c: In function ‘xc_core_arch_map_p2m_tree_rw’:
>> xg_core_x86.c:300:5: error: leak of ‘p2m_frame_list_list’ [CWE-401] 
>> [-Werror=analyzer-malloc-leak]
>>  300 |     return p2m_frame_list;
>>      |     ^~~~~~
>>  ‘xc_core_arch_map_p2m_writable’: events 1-2
>>    |
>>    |  378 | xc_core_arch_map_p2m_writable(xc_interface *xch, struct 
>> domain_info_context *dinfo, xc_dominfo_t *info,
>>    |      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>    |      | |
>>    |      | (1) entry to ‘xc_core_arch_map_p2m_writable’
>>    |......
>>    |  381 |     return xc_core_arch_map_p2m_rw(xch, dinfo, info, 
>> live_shinfo, live_p2m, 1);
>>    |      |            
>> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>    |      |            |
>>    |      |            (2) calling ‘xc_core_arch_map_p2m_rw’ from 
>> ‘xc_core_arch_map_p2m_writable’
>>    |
>>    +--> ‘xc_core_arch_map_p2m_rw’: events 3-10
>>           |
>>           |  319 | xc_core_arch_map_p2m_rw(xc_interface *xch, struct 
>> domain_info_context *dinfo, xc_dominfo_t *info,
>>           |      | ^~~~~~~~~~~~~~~~~~~~~~~
>>           |      | |
>>           |      | (3) entry to ‘xc_core_arch_map_p2m_rw’
>>           |......
>>           |  328 |     if ( xc_domain_nr_gpfns(xch, info->domid, 
>> &dinfo->p2m_size) < 0 )
>>           |      |        ~
>>           |      |        |
>>           |      |        (4) following ‘false’ branch...
>>           |......
>>           |  334 |     if ( dinfo->p2m_size < info->nr_pages  )
>>           |      |     ~~ ~
>>           |      |     |  |
>>           |      |     |  (6) following ‘false’ branch...
>>           |      |     (5) ...to here
>>           |......
>>           |  340 |     p2m_cr3 = GET_FIELD(live_shinfo, arch.p2m_cr3, 
>> dinfo->guest_width);
>>           |      |     ~~~~~~~
>>           |      |     |
>>           |      |     (7) ...to here
>>           |  341 |
>>           |  342 |     p2m_frame_list = p2m_cr3 ? 
>> xc_core_arch_map_p2m_list_rw(xch, dinfo, dom, live_shinfo, p2m_cr3)
>>           |      |                      
>> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>           |  343 |                              : 
>> xc_core_arch_map_p2m_tree_rw(xch, dinfo, dom, live_shinfo);
>>           |      |                              
>> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>           |      |                              | |
>>           |      |                              | (9) ...to here
>>           |      |                              | (10) calling 
>> ‘xc_core_arch_map_p2m_tree_rw’ from ‘xc_core_arch_map_p2m_rw’
>>           |      |                              (8) following ‘false’ 
>> branch...
>>           |
>>           +--> ‘xc_core_arch_map_p2m_tree_rw’: events 11-24
>>                  |
>>                  |  228 | xc_core_arch_map_p2m_tree_rw(xc_interface *xch, 
>> struct domain_info_context *dinfo,
>>                  |      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>                  |      | |
>>                  |      | (11) entry to ‘xc_core_arch_map_p2m_tree_rw’
>>                  |......
>>                  |  245 |     if ( !live_p2m_frame_list_list )
>>                  |      |        ~
>>                  |      |        |
>>                  |      |        (12) following ‘false’ branch (when 
>> ‘live_p2m_frame_list_list’ is non-NULL)...
>>                  |......
>>                  |  252 |     if ( !(p2m_frame_list_list = 
>> malloc(PAGE_SIZE)) )
>>                  |      |     ~~ ~                         ~~~~~~~~~~~~~~~~~
>>                  |      |     |  |                         |
>>                  |      |     |  |                         (14) allocated 
>> here
>>                  |      |     |  (15) assuming ‘p2m_frame_list_list’ is 
>> non-NULL
>>                  |      |     |  (16) following ‘false’ branch (when 
>> ‘p2m_frame_list_list’ is non-NULL)...
>>                  |      |     (13) ...to here
>>                  |......
>>                  |  257 |     memcpy(p2m_frame_list_list, 
>> live_p2m_frame_list_list, PAGE_SIZE);
>>                  |      |     ~~~~~~
>>                  |      |     |
>>                  |      |     (17) ...to here
>>                  |......
>>                  |  266 |     else if ( dinfo->guest_width < sizeof(unsigned 
>> long) )
>>                  |      |             ~
>>                  |      |             |
>>                  |      |             (18) following ‘false’ branch...
>>                  |......
>>                  |  270 |     live_p2m_frame_list =
>>                  |      |     ~~~~~~~~~~~~~~~~~~~
>>                  |      |     |
>>                  |      |     (19) ...to here
>>                  |......
>>                  |  275 |     if ( !live_p2m_frame_list )
>>                  |      |        ~
>>                  |      |        |
>>                  |      |        (20) following ‘false’ branch (when 
>> ‘live_p2m_frame_list’ is non-NULL)...
>>                  |......
>>                  |  282 |     if ( !(p2m_frame_list = 
>> malloc(P2M_TOOLS_FL_SIZE)) )
>>                  |      |     ~~ ~
>>                  |      |     |  |
>>                  |      |     |  (22) following ‘false’ branch (when 
>> ‘p2m_frame_list’ is non-NULL)...
>>                  |      |     (21) ...to here
>>                  |......
>>                  |  287 |     memset(p2m_frame_list, 0, P2M_TOOLS_FL_SIZE);
>>                  |      |     ~~~~~~
>>                  |      |     |
>>                  |      |     (23) ...to here
>>                  |......
>>                  |  300 |     return p2m_frame_list;
>>                  |      |     ~~~~~~
>>                  |      |     |
>>                  |      |     (24) ‘p2m_frame_list_list’ leaks here; was 
>> allocated at (14)
>>                  |
>> ```
>> Fixes: bd7a29c3d0 ("tools/libs/ctrl: fix xc_core_arch_map_p2m() to support 
>> linear p2m table")
>> 
>> Signed-off-by: Edwin Török <edwin.torok@xxxxxxxxx>
>> ---
>> tools/libs/guest/xg_core_x86.c | 2 ++
>> 1 file changed, 2 insertions(+)
>> 
>> diff --git a/tools/libs/guest/xg_core_x86.c b/tools/libs/guest/xg_core_x86.c
>> index 61106b98b8..69929879d7 100644
>> --- a/tools/libs/guest/xg_core_x86.c
>> +++ b/tools/libs/guest/xg_core_x86.c
>> @@ -297,6 +297,8 @@ xc_core_arch_map_p2m_tree_rw(xc_interface *xch, struct 
>> domain_info_context *dinf
>> 
>>     dinfo->p2m_frames = P2M_FL_ENTRIES;
>> 
>> +    free(p2m_frame_list_list);
>> +
>>     return p2m_frame_list;
>> 
>>  out:
> 
> I agree there are problems here, but I think they're larger still.  The
> live_p2m_frame_list_list and live_p2m_frame_list foreign mappings are
> leaked too on the success path.


I thought the goal of that function was to create the mapping (judging by its 
name), but looking at its caller there is another map_foreign_pages there,
so there is indeed a leak (-fanalyzer won't be able to spot these unless we 
figure out a way to put some attributs on these map and unmap to teach it that 
they are alloc/free pairs).

Pushed updated commits here (top 2): leak-fixes 
<https://github.com/edwintorok/xen/commits/leak-fixes>
Before posting a V2 I'll try it out on an actual machine though, just to check 
that we don't have a double-free instead now.

Thanks,
--Edwin

> 
> I think this is the necessary fix:
> 
> ~Andrew
> 
> ----8<----
> 
> diff --git a/tools/libs/guest/xg_core_x86.c b/tools/libs/guest/xg_core_x86.c
> index 61106b98b877..c5e4542ccccc 100644
> --- a/tools/libs/guest/xg_core_x86.c
> +++ b/tools/libs/guest/xg_core_x86.c
> @@ -229,11 +229,11 @@ xc_core_arch_map_p2m_tree_rw(xc_interface *xch,
> struct domain_info_context *dinf
>                               uint32_t dom, shared_info_any_t *live_shinfo)
>  {
>      /* Double and single indirect references to the live P2M table */
> -    xen_pfn_t *live_p2m_frame_list_list;
> +    xen_pfn_t *live_p2m_frame_list_list = NULL;
>      xen_pfn_t *live_p2m_frame_list = NULL;
>      /* Copies of the above. */
>      xen_pfn_t *p2m_frame_list_list = NULL;
> -    xen_pfn_t *p2m_frame_list;
> +    xen_pfn_t *p2m_frame_list = NULL;
>  
>      int err;
>      int i;
> @@ -297,8 +297,6 @@ xc_core_arch_map_p2m_tree_rw(xc_interface *xch,
> struct domain_info_context *dinf
>  
>      dinfo->p2m_frames = P2M_FL_ENTRIES;
>  
> -    return p2m_frame_list;
> -
>   out:
>      err = errno;
>  
> @@ -312,7 +310,7 @@ xc_core_arch_map_p2m_tree_rw(xc_interface *xch,
> struct domain_info_context *dinf
>  
>      errno = err;
>  
> -    return NULL;
> +    return p2m_frame_list;
>  }
>  
>  static int
> 




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.