_ishm prereserves address space for _ODP_ISHM_SINGLE_VA allocated memory
by reserving unreachable "memory", using PROT_NONE, hence really allocating
virtual space (as the memory cannot be hit).
The problem came when trying to use some of this preallocated space:
strangely, if a new mapping on the preallocated virtual space failed (e.g.
due to lack of huge pages), linux would returned MAP_FAILED (OK) but
also cancel the previous PROT_NONE mapping, hence making the virtual
memory space available for further mmaps. (code Point A)
Before this patch, the code simply re-reserved (PROT_NONE) the area
(point B):
This was NOT OK: yes, _ishm_reserve calls are mutexed, so no other
odpthread 2 could do a reserve between point A and B of opdthread1, but if
thread2 did its own mmap(), malloc(),...,  there was a chance for thread 2
to get virtual space from the preserved area (which should be blocked).
This patch does not allow any A-B window by first doing an mmap (non fixed)
and then performing a second mapping at the correct address (in the
pre-reserved area), which is guaranteed to succeed, and finally removing
the non-fixed mapping.
The non-fix mapping just acts as a failure guard when the proper maping
is done.
Fixes https://bugs.linaro.org/show_bug.cgi?id=2834
(but very hard to trigger i.e. to prove)

Signed-off-by: Christophe Milard <christophe.mil...@linaro.org>
---
 platform/linux-generic/_ishmphy.c | 42 +++++++++++++++++++++++++++++----------
 1 file changed, 32 insertions(+), 10 deletions(-)

diff --git a/platform/linux-generic/_ishmphy.c 
b/platform/linux-generic/_ishmphy.c
index 2b2d100..d519af6 100644
--- a/platform/linux-generic/_ishmphy.c
+++ b/platform/linux-generic/_ishmphy.c
@@ -94,7 +94,7 @@ int _odp_ishmphy_unbook_va(void)
 void *_odp_ishmphy_map(int fd, void *start, uint64_t size,
                       int flags)
 {
-       void *mapped_addr;
+       void *mapped_addr_tmp, *mapped_addr;
        int mmap_flags = 0;
 
        if (flags & _ODP_ISHM_SINGLE_VA) {
@@ -103,15 +103,37 @@ void *_odp_ishmphy_map(int fd, void *start, uint64_t size,
                        return NULL;
                }
                /* maps over fragment of reserved VA: */
-               mapped_addr = mmap(start, size, PROT_READ | PROT_WRITE,
-                                  MAP_SHARED | MAP_FIXED | mmap_flags, fd, 0);
-               /* if mapping fails, re-block the space we tried to take
-                * as it seems a mapping failure still affect what was there??*/
-               if (mapped_addr == MAP_FAILED) {
-                       mmap_flags = MAP_SHARED | MAP_FIXED |
-                                    MAP_ANONYMOUS | MAP_NORESERVE;
-                       mmap(start, size, PROT_NONE, mmap_flags, -1, 0);
-                       mprotect(start, size, PROT_NONE);
+               /* first, try a normal map. If that works, remap it where it
+                * should (on the prereverved space), and remove the initial
+                * normal mapping:
+                * This is because it turned out that if a mapping fails
+                * on a the prereserved virtual address space, then
+                * the prereserved address space which was tried to be mapped
+                * on becomes available to the kernel again! This was not
+                * according to expectations: the assumption was that if a
+                * mapping fails, the system should remain unchanged, but this
+                * is obvioulsy not true (at least for huge pages when
+                * exhausted).
+                * So the strategy is to first map at a non reserved place
+                * (which can then be freed and returned to the kernel on
+                * failure) and peform a new map to the prereserved space on
+                * success (which is then guaranteed to work).
+                * The initial free maping can then be removed.
+                */
+               mapped_addr = MAP_FAILED;
+               mapped_addr_tmp = mmap(NULL, size, PROT_READ | PROT_WRITE,
+                                      MAP_SHARED | mmap_flags, fd, 0);
+               if (mapped_addr_tmp != MAP_FAILED) {
+                       /* If OK, do new map at right fixed location... */
+                       mapped_addr = mmap(start,
+                                          size, PROT_READ | PROT_WRITE,
+                                          MAP_SHARED | MAP_FIXED | mmap_flags,
+                                          fd, 0);
+                       if (mapped_addr != start)
+                               ODP_ERR("new map failed:%s\n", strerror(errno));
+                       /* ... and remove initial mapping: */
+                       if (munmap(mapped_addr_tmp, size))
+                               ODP_ERR("munmap failed:%s\n", strerror(errno));
                }
        } else {
                /* just do a new mapping in the VA space: */
-- 
2.7.4

Reply via email to