Skip to contentSkip to conversation

Missing checks for xc_map_foreign_range() failure

PATCH In review Markus Armbruster 5 participants 13 messages Aug 31, 2007 – Jan 24, 2008

lore
Share & tools
lore
#0 lore raw

privcmd_ioctl() case IOCTL_PRIVCMD_MMAPBATCH has two different ways to indicate failure:

1. The usual way: do nothing and return error code.

2. An unusual way: set the most significant four bits of those elements of the mfn array argument that could not be mapped, and return 0.

When the array isn't writable, the error information is simply discarded.

xc_map_foreign_range() behaves exactly the same. Its second failure mode isn't documented anywhere I can see. Doc bug.

Some users of xc_map_foreign_range() search the array for failed mfns. Others don't. All check the return value.

There might be uses for the second failure mode of xc_map_foreign_range() which justify this awkward API. qemu_remap_bucket() looks like such a case.

Other, more naive uses would profit from a less awkward convenience function that either succeeds completely or fails completely. As far as I can see, these uses all neglect to check for the second failure mode so far. Potential crash bugs.

#1 lore raw
Quoted 4 lines · Markus Armbruster

Other, more naive uses would profit from a less awkward convenience function that either succeeds completely or fails completely. As far as I can see, these uses all neglect to check for the second failure mode so far. Potential crash bugs.

Yes, that makes sense. Are you proposing to make a patch?

-- Keir

#2 lore raw
Quoted 8 lines · Keir Fraser
Quoted 4 lines · Markus Armbruster

Other, more naive uses would profit from a less awkward convenience function that either succeeds completely or fails completely. As far as I can see, these uses all neglect to check for the second failure mode so far. Potential crash bugs.

Yes, that makes sense. Are you proposing to make a patch?

Happy to do that :)

#3 lore raw

hi
what is the function of the xc_map_foreign_range(), what does it work for ? could you give me someexplanation or example about it , i encounter it in the code of xc_domain_save(), and confused about the function of it,could you help me

Thanks in advance

Quoted 23 lines · Markus Armbruster
Quoted 10 lines · Keir Fraser
Quoted 5 lines · Markus Armbruster

Other, more naive uses would profit from a less awkward convenience function that either succeeds completely or fails completely. As far as I can see, these uses all neglect to check for the second failure mode so far. Potential crash bugs.

Yes, that makes sense. Are you proposing to make a patch?

Happy to do that :)

_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel

#4 lore raw
Quoted 7 lines · tgh

hi
what is the function of the xc_map_foreign_range(), what does it work for ? could you give me someexplanation or example about it , i encounter it in the code of xc_domain_save(), and confused about the function of it,could you help me

Thanks in advance

xc_map_foreign_{range,pages,batch}() let you map another domain's memory, similar to mmap().

xc_map_foreign_range() is the simplest of them: it maps a range of memory starting at a mfn. You typically map one page that way, i.e. you pass PAGE_SIZE as size argument.

xc_domain_save() uses it to map the domain's shared info page. That's a well-known page, and its mfn can be found in the domain's info obtained from xc_domain_getinfo().

#5 lore raw

PATCH 1/1 xc_map_foreign_pages(), a convenient alternative to xc_map_foreign_batch()

In reply to Keir Fraser

xc_map_foreign_batch() can succeed partially. It is awkward to use when you're only interested in complete success. Provide new xc_map_foreign_pages() convenience function for that kind of use. Also convert two obvious calls to use it.

---
The patch converts only those uses of xc_map_foreign_batch() to xc_map_foreign_pages() where I'm 100% sure it's safe. For the others, I'd rather have a second opinion from somebody familiar with the code.

Discussion of uses:

* qemu_remap_bucket() in tools/ioemu/vl.c

Tests for mapping failure, although in a slightly sloppy way:

      !(pfns[i + --j] & 0xF0000000UL)

This is true when any of the four bits is set. But mapping failed only when all of them are set, so maybe it should do this instead:

      (pfns[i + --j] & 0xF0000000UL) != 0xF0000000UL

* main() in tools/ioemu/vl.c, in __ia64__ code

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages().

* set_vram_mapping() in tools/ioemu/hw/cirrus_vga.c

Doesn't test for mapping failure. Patch converts this one to xc_map_foreign_pages().

* copy_from_GFW_to_nvram() in tools/libxc/ia64/xc_ia64_hvm_build.c

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages(), but I have no way to test it.

* xc_core_arch_map_p2m() in tools/libxc/xc_core_x86.c

This maps a two level page directory: first a a page of pfns for the page directory (live_p2m_frame_list_list), using that pages of pfns for the pages themselves (live_p2m_frame_list), and using that the actual pages.

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages().

* xc_domain_restore() in tools/libxc/xc_domain_restore.c

Three uses, none of them tests for mapping failure.

I don't understand this code.

The first use of xc_map_foreign_batch() appears to deliberately pass invalid PFNs.

* map_and_save_p2m_table() in tools/libxc/xc_domain_save.c

Similar to xc_core_arch_map_p2m().

* xc_domain_save() in tools/libxc/xc_domain_save.c

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages().

* xc_domain_resume_any() in tools/libxc/xc_resume.c

Similar to xc_core_arch_map_p2m().

* xenfb_map_fb() in tools/xenfb/xenfb.c

Another map through a two level page directory, roughly similar to xc_core_arch_map_p2m().

Doesn't test for mapping failure. Patch converts this one to xc_map_foreign_pages().

tools/ioemu/hw/cirrus_vga.c +1 -1
diff -r 256160ff19b7 tools/ioemu/hw/cirrus_vga.c
@@ -2561,7 +2561,7 @@ static void *set_vram_mapping(unsigned l
 
     set_mm_mapping(xc_handle, domid, nr_extents, 0, extent_start);
 
-    vram_pointer = xc_map_foreign_batch(xc_handle, domid,
+    vram_pointer = xc_map_foreign_pages(xc_handle, domid,
                                         PROT_READ|PROT_WRITE,
                                         extent_start, nr_extents);
     if (vram_pointer == NULL) {
tools/libxc/xc_misc.c +33 -0
diff -r 256160ff19b7 tools/libxc/xc_misc.c
@@ -224,6 +224,39 @@ int xc_hvm_set_pci_link_route(
     unlock_pages(&arg, sizeof(arg));
 
     return rc;
+}
+
+void *xc_map_foreign_pages(int xc_handle, uint32_t dom, int prot,
+                           const xen_pfn_t *arr, int num)
+{
+    xen_pfn_t *pfn;
+    void *res;
+    int i;
+
+    pfn = malloc(num * sizeof(*pfn));
+    if (!pfn)
+        return NULL;
+    memcpy(pfn, arr, num * sizeof(*pfn));
+
+    res = xc_map_foreign_batch(xc_handle, dom, prot, pfn, num);
+    if (res) {
+        for (i = 0; i < num; i++) {
+            if ((pfn[i] & 0xF0000000UL) == 0xF0000000UL) {
+                /*
+                 * xc_map_foreign_batch() doesn't give us an error
+                 * code, so we have to make one up.  May not be the
+                 * appropriate one.
+                 */
+                errno = EINVAL;
+                munmap(res, num * PAGE_SIZE);
+                res = NULL;
+                break;
+            }
+        }
+    }
+
+    free(pfn);
+    return res;
 }
 
 /*
tools/libxc/xenctrl.h +8 -0
diff -r 256160ff19b7 tools/libxc/xenctrl.h
@@ -646,6 +646,14 @@ void *xc_map_foreign_range(int xc_handle
                             int size, int prot,
                             unsigned long mfn );
 
+void *xc_map_foreign_pages(int xc_handle, uint32_t dom, int prot,
+                           const xen_pfn_t *arr, int num );
+
+/**
+ * Like xc_map_foreign_pages(), except it can succeeed partially.
+ * When a page cannot be mapped, its PFN in @arr is or'ed with
+ * 0xF0000000 to indicate the error.
+ */
 void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot,
                            xen_pfn_t *arr, int num );
 
tools/xenfb/xenfb.c +2 -8
diff -r 256160ff19b7 tools/xenfb/xenfb.c
@@ -398,21 +398,15 @@ static int xenfb_map_fb(struct xenfb_pri
 	if (!pgmfns || !fbmfns)
 		goto out;
 
-	/*
-	 * Bug alert: xc_map_foreign_batch() can fail partly and
-	 * return a non-null value.  This is a design flaw.  When it
-	 * happens, we happily continue here, and later crash on
-	 * access.
-	 */
 	xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
-	map = xc_map_foreign_batch(xenfb->xc, domid,
+	map = xc_map_foreign_pages(xenfb->xc, domid,
 				   PROT_READ, pgmfns, n_fbdirs);
 	if (map == NULL)
 		goto out;
 	xenfb_copy_mfns(mode, n_fbmfns, fbmfns, map);
 	munmap(map, n_fbdirs * XC_PAGE_SIZE);
 
-	xenfb->pub.pixels = xc_map_foreign_batch(xenfb->xc, domid,
+	xenfb->pub.pixels = xc_map_foreign_pages(xenfb->xc, domid,
 				PROT_READ | PROT_WRITE, fbmfns, n_fbmfns);
 	if (xenfb->pub.pixels == NULL)
 		goto out;
#6 lore raw

RE: [PATCH] xc_map_foreign_pages(), a convenient alternative to xc_map_foreign_batch()

Just curious why this can't be fixed in kernel driver? Is there case that caller is tolerable with partial mapping?

Also why do you need an extra allocation and copy to arr?

Thanks,
Kevin

Quoted 191 lines

From: Markus Armbruster
Sent: 2007年9月6日 0:37

xc_map_foreign_batch() can succeed partially. It is awkward to use when you're only interested in complete success. Provide new xc_map_foreign_pages() convenience function for that kind of use. Also convert two obvious calls to use it.

---
The patch converts only those uses of xc_map_foreign_batch() to xc_map_foreign_pages() where I'm 100% sure it's safe. For the others, I'd rather have a second opinion from somebody familiar with the code.

Discussion of uses:

* qemu_remap_bucket() in tools/ioemu/vl.c

Tests for mapping failure, although in a slightly sloppy way:

     !(pfns[i + --j] & 0xF0000000UL)

This is true when any of the four bits is set. But mapping failed only when all of them are set, so maybe it should do this instead:

     (pfns[i + --j] & 0xF0000000UL) != 0xF0000000UL

* main() in tools/ioemu/vl.c, in __ia64__ code

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages().

* set_vram_mapping() in tools/ioemu/hw/cirrus_vga.c

Doesn't test for mapping failure. Patch converts this one to xc_map_foreign_pages().

* copy_from_GFW_to_nvram() in tools/libxc/ia64/xc_ia64_hvm_build.c

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages(), but I have no way to test it.

* xc_core_arch_map_p2m() in tools/libxc/xc_core_x86.c

This maps a two level page directory: first a a page of pfns for the page directory (live_p2m_frame_list_list), using that pages of pfns for the pages themselves (live_p2m_frame_list), and using that the actual pages.

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages().

* xc_domain_restore() in tools/libxc/xc_domain_restore.c

Three uses, none of them tests for mapping failure.

I don't understand this code.

The first use of xc_map_foreign_batch() appears to deliberately pass invalid PFNs.

* map_and_save_p2m_table() in tools/libxc/xc_domain_save.c

Similar to xc_core_arch_map_p2m().

* xc_domain_save() in tools/libxc/xc_domain_save.c

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages().

* xc_domain_resume_any() in tools/libxc/xc_resume.c

Similar to xc_core_arch_map_p2m().

* xenfb_map_fb() in tools/xenfb/xenfb.c

Another map through a two level page directory, roughly similar to xc_core_arch_map_p2m().

Doesn't test for mapping failure. Patch converts this one to xc_map_foreign_pages().

diff -r 256160ff19b7 tools/ioemu/hw/cirrus_vga.c

tools/ioemu/hw/cirrus_vga.c +1 -1
@@ -2561,7 +2561,7 @@ static void *set_vram_mapping(unsigned l
 
    set_mm_mapping(xc_handle, domid, nr_extents, 0, extent_start);
 
-    vram_pointer = xc_map_foreign_batch(xc_handle, domid,
+    vram_pointer = xc_map_foreign_pages(xc_handle, domid,
 
 PROT_READ|PROT_WRITE,
                                        extent_start, nr_extents);
    if (vram_pointer == NULL) {

diff -r 256160ff19b7 tools/libxc/xc_misc.c

tools/libxc/xc_misc.c +21 -0
@@ -224,6 +224,39 @@ int xc_hvm_set_pci_link_route(
    unlock_pages(&arg, sizeof(arg));
 
    return rc;
+}
+
+void *xc_map_foreign_pages(int xc_handle, uint32_t dom, int prot,
+                           const xen_pfn_t *arr, int num)
+{
+    xen_pfn_t *pfn;
+    void *res;
+    int i;
+
+    pfn = malloc(num * sizeof(*pfn));
+    if (!pfn)
+        return NULL;
+    memcpy(pfn, arr, num * sizeof(*pfn));
+
+    res = xc_map_foreign_batch(xc_handle, dom, prot, pfn, num);
+    if (res) {
+        for (i = 0; i < num; i++) {
+            if ((pfn[i] & 0xF0000000UL) == 0xF0000000UL) {
+                /*
+                 * xc_map_foreign_batch() doesn't give us an error
+                 * code, so we have to make one up.  May not be

the

tools/libxc/xc_misc.c +12 -0
+                 * appropriate one.
+                 */
+                errno = EINVAL;
+                munmap(res, num * PAGE_SIZE);
+                res = NULL;
+                break;
+            }
+        }
+    }
+
+    free(pfn);
+    return res;

}

/*
diff -r 256160ff19b7 tools/libxc/xenctrl.h

tools/libxc/xenctrl.h +8 -0
@@ -646,6 +646,14 @@ void *xc_map_foreign_range(int xc_handle
                            int size, int prot,
                            unsigned long mfn );
 
+void *xc_map_foreign_pages(int xc_handle, uint32_t dom, int prot,
+                           const xen_pfn_t *arr, int num );
+
+/**
+ * Like xc_map_foreign_pages(), except it can succeeed partially.
+ * When a page cannot be mapped, its PFN in @arr is or'ed with
+ * 0xF0000000 to indicate the error.
+ */
 void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot,
                           xen_pfn_t *arr, int num );
 

diff -r 256160ff19b7 tools/xenfb/xenfb.c

tools/xenfb/xenfb.c +2 -8
@@ -398,21 +398,15 @@ static int xenfb_map_fb(struct xenfb_pri
 	if (!pgmfns || !fbmfns)
 		goto out;
 
-	/*
-	 * Bug alert: xc_map_foreign_batch() can fail partly and
-	 * return a non-null value.  This is a design flaw.  When it
-	 * happens, we happily continue here, and later crash on
-	 * access.
-	 */
 	xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
-	map = xc_map_foreign_batch(xenfb->xc, domid,
+	map = xc_map_foreign_pages(xenfb->xc, domid,
 				   PROT_READ, pgmfns, n_fbdirs);
 	if (map == NULL)
 		goto out;
 	xenfb_copy_mfns(mode, n_fbmfns, fbmfns, map);
 	munmap(map, n_fbdirs * XC_PAGE_SIZE);
 
-	xenfb->pub.pixels = xc_map_foreign_batch(xenfb->xc, domid,
+	xenfb->pub.pixels = xc_map_foreign_pages(xenfb->xc, domid,
 				PROT_READ | PROT_WRITE, fbmfns, n_fbmfns);
 	if (xenfb->pub.pixels == NULL)
 		goto out;

_______________________________________________
Xen-devel mailing list
[email protected]
http://lists.xensource.com/xen-devel

#7 lore raw

Re: [PATCH] xc_map_foreign_pages(),a convenient alternative to xc_map_foreign_batch()

Quoted 2 lines · Kevin

Just curious why this can't be fixed in kernel driver? Is there case that caller is tolerable with partial mapping?

We would need an extra command in the kernel driver, as the current partial-success semantics are used appropriately by some callers. Doing it in libxc avoids requiring a kernel upgrade to fix this issue.

-- Keir

#8 lore raw

RE: [PATCH] xc_map_foreign_pages(), a convenient alternative to xc_map_foreign_batch()

Quoted 8 lines

From: Keir Fraser [mailto:[email protected]]
Sent: 2007年9月6日 16:47

Quoted 2 lines · Kevin

Just curious why this can't be fixed in kernel driver? Is there case that caller is tolerable with partial mapping?

Quoted 6 lines

We would need an extra command in the kernel driver, as the current partial-success semantics are used appropriately by some callers. Doing it
in libxc avoids requiring a kernel upgrade to fix this issue.

-- Keir

Interesting... any example I can learn? Yes, compatibility is one concern.

Thanks,
Kevin

#9 lore raw

Re: [PATCH] xc_map_foreign_pages(), a convenient alternative to xc_map_foreign_batch()

Quoted 13 lines · Kevin
Quoted 13 lines

From: Keir Fraser [mailto:[email protected]]
Sent: 2007年9月6日 16:47

Quoted 2 lines · Kevin

Just curious why this can't be fixed in kernel driver? Is there case that caller is tolerable with partial mapping?

We would need an extra command in the kernel driver, as the current partial-success semantics are used appropriately by some callers. Doing it
in libxc avoids requiring a kernel upgrade to fix this issue.

Quoted 6 lines
Quoted 1 line

-- Keir

Interesting... any example I can learn? Yes, compatibility is one concern.

Thanks,
Kevin

Have a look at qemu_remap_bucket() in tools/ioemu/vl.c.

#10 lore raw

Re: [PATCH] xc_map_foreign_pages(), a convenient alternative to xc_map_foreign_batch()

In reply to Tian, Kevin

"Tian, Kevin" <[email protected]> writes:

[...]

Quoted 1 line

Also why do you need an extra allocation and copy to arr?

To avoid clobbering the arr argument, which I feel would be a rather bad API.

[...]

#11 lore raw

Re: [Xen-devel] [PATCH] xc_map_foreign_pages(), a convenient alternative to xc_map_foreign_batch()

In reply to Markus Armbruster
Quoted 5 lines · Markus Armbruster

The patch converts only those uses of xc_map_foreign_batch() to xc_map_foreign_pages() where I'm 100% sure it's safe. For the others, I'd rather have a second opinion from somebody familiar with the code.

Discussion of uses:

...

Quoted 4 lines

* main() in tools/ioemu/vl.c, in __ia64__ code

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages().

This can't be replaced because some pages, e.g. VGA area [0xA0000, 0xC0000), can't be mapped.

Quoted 4 lines

* copy_from_GFW_to_nvram() in tools/libxc/ia64/xc_ia64_hvm_build.c

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages(), but I have no way to test it.

This can be replaced with xc_map_foreign_pages()
This change will go through xen-ia64-devel.hg.
I'll send the patch to xen-ia64-devel.

Quoted 9 lines

* xc_core_arch_map_p2m() in tools/libxc/xc_core_x86.c

This maps a two level page directory: first a a page of pfns for the page directory (live_p2m_frame_list_list), using that pages of pfns for the pages themselves (live_p2m_frame_list), and using that the actual pages.

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages().

This can be replaced with xc_map_foreign_pages().

Signature · 1 line
yamahata
#12 lore raw

Re: [PATCH] xc_map_foreign_pages(), a convenient alternative to xc_map_foreign_batch()

Quoted 6 lines · Isaku Yamahata
Quoted 5 lines · Markus Armbruster

The patch converts only those uses of xc_map_foreign_batch() to xc_map_foreign_pages() where I'm 100% sure it's safe. For the others, I'd rather have a second opinion from somebody familiar with the code.

Discussion of uses:

Quoted 8 lines

...

Quoted 4 lines

* main() in tools/ioemu/vl.c, in __ia64__ code

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages().

This can't be replaced because some pages, e.g. VGA area [0xA0000, 0xC0000), can't be mapped.

Is it okay not to check for success?

Quoted 8 lines
Quoted 4 lines

* copy_from_GFW_to_nvram() in tools/libxc/ia64/xc_ia64_hvm_build.c

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages(), but I have no way to test it.

This can be replaced with xc_map_foreign_pages()
This change will go through xen-ia64-devel.hg.
I'll send the patch to xen-ia64-devel.

Thanks!

Quoted 11 lines
Quoted 9 lines

* xc_core_arch_map_p2m() in tools/libxc/xc_core_x86.c

This maps a two level page directory: first a a page of pfns for the page directory (live_p2m_frame_list_list), using that pages of pfns for the pages themselves (live_p2m_frame_list), and using that the actual pages.

Doesn't test for mapping failure. Likely candidate for xc_map_foreign_pages().

This can be replaced with xc_map_foreign_pages().

Obvious patch for that:

tools/libxc/xc_core_x86.c +2 -2
diff -r 9071521d4864 tools/libxc/xc_core_x86.c
@@ -86,7 +86,7 @@ xc_core_arch_map_p2m(int xc_handle, xc_d
     }
 
     live_p2m_frame_list =
-        xc_map_foreign_batch(xc_handle, dom, PROT_READ,
+        xc_map_foreign_pages(xc_handle, dom, PROT_READ,
                              live_p2m_frame_list_list,
                              P2M_FLL_ENTRIES);
 
@@ -96,7 +96,7 @@ xc_core_arch_map_p2m(int xc_handle, xc_d
         goto out;
     }
 
-    *live_p2m = xc_map_foreign_batch(xc_handle, dom, PROT_READ,
+    *live_p2m = xc_map_foreign_pages(xc_handle, dom, PROT_READ,
                                     live_p2m_frame_list,
                                     P2M_FL_ENTRIES);