Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
smpi_memory: don't copy poisoned regions when AddressSanitizer is in use.
[simgrid.git] / src / smpi / internals / smpi_memory.cpp
1 /* Copyright (c) 2015-2017. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include <cstdint>
7 #include <climits>
8 #include <cstring>
9
10 #include <vector>
11
12 #include <cerrno>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <cstring>
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19
20 #ifndef WIN32
21 #include <sys/mman.h>
22 #include <unistd.h>
23
24 #include "src/xbt/memory_map.hpp"
25
26 #include "private.h"
27 #include "private.hpp"
28
29 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(smpi_memory, smpi, "Memory layout support for SMPI");
30
31 int smpi_loaded_page = -1;
32 char* smpi_start_data_exe = nullptr;
33 int smpi_size_data_exe = 0;
34 int smpi_privatize_global_variables;
35
36 static const int PROT_RWX = (PROT_READ | PROT_WRITE | PROT_EXEC);
37 static const int PROT_RW  = (PROT_READ | PROT_WRITE );
38 XBT_ATTRIB_UNUSED static const int PROT_RX  = (PROT_READ | PROT_EXEC );
39
40 void smpi_get_executable_global_size()
41 {
42   char buffer[PATH_MAX];
43   char* full_name = realpath(xbt_binary_name, buffer);
44   if (full_name == nullptr)
45     xbt_die("Could not resolve binary file name");
46
47   std::vector<simgrid::xbt::VmMap> map = simgrid::xbt::get_memory_map(getpid());
48   for (auto i = map.begin(); i != map.end() ; ++i) {
49     // TODO, In practice, this implementation would not detect a completely
50     // anonymous data segment. This does not happen in practice, however.
51
52     // File backed RW entry:
53     if (i->pathname == full_name && (i->prot & PROT_RWX) == PROT_RW) {
54       smpi_start_data_exe = (char*) i->start_addr;
55       smpi_size_data_exe = i->end_addr - i->start_addr;
56       ++i;
57       /* Here we are making the assumption that a suitable empty region
58          following the rw- area is the end of the data segment. It would
59          be better to check with the size of the data segment. */
60       if (i != map.end() && i->pathname.empty() && (i->prot & PROT_RWX) == PROT_RW
61           && (char*)i->start_addr ==  smpi_start_data_exe + smpi_size_data_exe) {
62         smpi_size_data_exe = (char*)i->end_addr - smpi_start_data_exe;
63       }
64       return;
65     }
66   }
67   xbt_die("Did not find my data segment.");
68 }
69 #endif
70
71 #if !defined(__has_feature)
72 #define __has_feature(x) 0
73 #endif
74 #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
75 #include <sanitizer/asan_interface.h>
76 void* safe_memcpy(void* dest, void* src, size_t n)
77 {
78   char* psrc  = static_cast<char*>(src);
79   char* pdest = static_cast<char*>(dest);
80   for (size_t i = 0; i < n;) {
81     while (i < n && __asan_address_is_poisoned(psrc + i))
82       ++i;
83     if (i < n) {
84       char* p  = static_cast<char*>(__asan_region_is_poisoned(psrc + i, n - i));
85       size_t j = p ? (p - psrc) : n;
86       memcpy(pdest + i, psrc + i, j - i);
87       i = j;
88     }
89   }
90   return dest;
91 }
92 #else
93 #define safe_memcpy(dest, src, n) memcpy(dest, src, n)
94 #endif
95
96 /** Map a given SMPI privatization segment (make a SMPI process active) */
97 void smpi_switch_data_segment(int dest) {
98   if (smpi_loaded_page == dest)//no need to switch, we've already loaded the one we want
99     return;
100
101   // So the job:
102   smpi_really_switch_data_segment(dest);
103 }
104
105 /** Map a given SMPI privatization segment (make a SMPI process active)  even if SMPI thinks it is already active
106  *
107  *  When doing a state restoration, the state of the restored variables  might not be consistent with the state of the
108  *  virtual memory. In this case, we to change the data segment.
109  */
110 void smpi_really_switch_data_segment(int dest)
111 {
112   if(smpi_size_data_exe == 0)//no need to switch
113     return;
114
115 #if HAVE_PRIVATIZATION
116   if(smpi_loaded_page==-1){//initial switch, do the copy from the real page here
117     for (int i=0; i< smpi_process_count(); i++){
118       safe_memcpy(smpi_privatization_regions[i].address, TOPAGE(smpi_start_data_exe), smpi_size_data_exe);
119     }
120   }
121
122   // FIXME, cross-process support (mmap across process when necessary)
123   int current = smpi_privatization_regions[dest].file_descriptor;
124   XBT_DEBUG("Switching data frame to the one of process %d", dest);
125   void* tmp =
126       mmap(TOPAGE(smpi_start_data_exe), smpi_size_data_exe, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, current, 0);
127   if (tmp != TOPAGE(smpi_start_data_exe))
128     xbt_die("Couldn't map the new region (errno %d): %s", errno, strerror(errno));
129   smpi_loaded_page = dest;
130 #endif
131 }
132
133 int smpi_is_privatization_file(char* file)
134 {
135   const std::string buffer_path("/dev/shm/my-buffer-");
136   return buffer_path.compare(0, std::string::npos, file, buffer_path.length()) == 0;
137 }
138
139 void smpi_initialize_global_memory_segments()
140 {
141
142 #if HAVE_PRIVATIZATION
143   smpi_get_executable_global_size();
144
145   XBT_DEBUG ("bss+data segment found : size %d starting at %p", smpi_size_data_exe, smpi_start_data_exe );
146
147   if (smpi_size_data_exe == 0){//no need to switch
148     smpi_privatize_global_variables=false;
149     return;
150   }
151
152   smpi_privatization_regions = static_cast<smpi_privatization_region_t>(
153       xbt_malloc(smpi_process_count() * sizeof(struct s_smpi_privatization_region)));
154
155   for (int i=0; i< smpi_process_count(); i++){
156     // create SIMIX_process_count() mappings of this size with the same data inside
157     int file_descriptor;
158     void* address = nullptr;
159     char path[24];
160     int status;
161
162     do {
163       snprintf(path, sizeof(path), "/smpi-buffer-%06x", rand() % 0xffffffU);
164       file_descriptor = shm_open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
165     } while (file_descriptor == -1 && errno == EEXIST);
166     if (file_descriptor < 0) {
167       if (errno == EMFILE) {
168         xbt_die("Impossible to create temporary file for memory mapping: %s\n\
169 The open() system call failed with the EMFILE error code (too many files). \n\n\
170 This means that you reached the system limits concerning the amount of files per process. \
171 This is not a surprise if you are trying to virtualize many processes on top of SMPI. \
172 Don't panic -- you should simply increase your system limits and try again. \n\n\
173 First, check what your limits are:\n\
174   cat /proc/sys/fs/file-max # Gives you the system-wide limit\n\
175   ulimit -Hn                # Gives you the per process hard limit\n\
176   ulimit -Sn                # Gives you the per process soft limit\n\
177   cat /proc/self/limits     # Displays any per-process limitation (including the one given above)\n\n\
178 If one of these values is less than the amount of MPI processes that you try to run, then you got the explanation of this error. \
179 Ask the Internet about tutorials on how to increase the files limit such as: https://rtcamp.com/tutorials/linux/increase-open-files-limit/",
180                 strerror(errno));
181       }
182       xbt_die("Impossible to create temporary file for memory mapping: %s", strerror(errno));
183     }
184
185     status = ftruncate(file_descriptor, smpi_size_data_exe);
186     if (status)
187       xbt_die("Impossible to set the size of the temporary file for memory mapping");
188
189     /* Ask for a free region */
190     address = mmap(nullptr, smpi_size_data_exe, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, 0);
191     if (address == MAP_FAILED)
192       xbt_die("Couldn't find a free region for memory mapping");
193
194     status = shm_unlink(path);
195     if (status)
196       xbt_die("Impossible to unlink temporary file for memory mapping");
197
198     // initialize the values
199     safe_memcpy(address, TOPAGE(smpi_start_data_exe), smpi_size_data_exe);
200
201     // store the address of the mapping for further switches
202     smpi_privatization_regions[i].file_descriptor = file_descriptor;
203     smpi_privatization_regions[i].address         = address;
204   }
205 #else /* ! HAVE_PRIVATIZATION */
206   smpi_privatize_global_variables = false;
207   xbt_die("You are trying to use privatization on a system that does not support it. Don't.");
208   return;
209 #endif
210 }
211
212 void smpi_destroy_global_memory_segments(){
213   if (smpi_size_data_exe == 0)//no need to switch
214     return;
215 #if HAVE_PRIVATIZATION
216   for (int i=0; i< smpi_process_count(); i++) {
217     if (munmap(smpi_privatization_regions[i].address, smpi_size_data_exe) < 0)
218       XBT_WARN("Unmapping of fd %d failed: %s", smpi_privatization_regions[i].file_descriptor, strerror(errno));
219     close(smpi_privatization_regions[i].file_descriptor);
220   }
221   xbt_free(smpi_privatization_regions);
222 #endif
223 }
224