1 // Licensed under BSD-MIT: See LICENSE.
7 #include <ccan/noerr/noerr.h>
13 #if HAVE_PROC_SELF_MAPS
14 static char *grab(const char *filename)
17 size_t max = 16384, s = 0;
20 fd = open(filename, O_RDONLY);
24 buffer = malloc(max+1);
28 while ((ret = read(fd, buffer + s, max - s)) > 0) {
31 buffer = realloc(buffer, max*2+1);
51 static char *skip_line(char *p)
53 char *nl = strchr(p, '\n');
59 static struct ptr_valid_map *add_map(struct ptr_valid_map *map,
62 unsigned long start, unsigned long end, bool is_write)
66 map = realloc(map, sizeof(*map) * *max);
70 map[*num].start = (void *)start;
71 map[*num].end = (void *)end;
72 map[*num].is_write = is_write;
77 static struct ptr_valid_map *get_proc_maps(unsigned int *num)
80 struct ptr_valid_map *map;
81 unsigned int max = 16;
83 buf = grab("/proc/self/maps");
89 map = malloc(sizeof(*map) * max);
94 for (p = buf; p && *p; p = skip_line(p)) {
95 unsigned long start, end;
98 /* Expect '<start-in-hex>-<end-in-hex> rw... */
99 start = strtoul(p, &endp, 16);
102 end = strtoul(endp+1, &endp, 16);
107 if (endp[0] != 'r' && endp[0] != '-')
109 if (endp[1] != 'w' && endp[1] != '-')
112 /* We only add readable mappings. */
113 if (endp[0] == 'r') {
114 map = add_map(map, num, &max, start, end,
133 static struct ptr_valid_map *get_proc_maps(unsigned int *num)
140 static bool check_with_maps(struct ptr_valid_batch *batch,
141 const char *p, size_t size, bool is_write)
145 for (i = 0; i < batch->num_maps; i++) {
146 if (p >= batch->maps[i].start && p < batch->maps[i].end) {
147 /* Overlap into other maps? Recurse with remainder. */
148 if (p + size > batch->maps[i].end) {
149 size_t len = p + size - batch->maps[i].end;
150 if (!check_with_maps(batch, batch->maps[i].end,
154 return !is_write || batch->maps[i].is_write;
160 static void finish_child(struct ptr_valid_batch *batch)
162 close(batch->to_child);
163 close(batch->from_child);
164 waitpid(batch->child_pid, NULL, 0);
165 batch->child_pid = 0;
168 static bool child_alive(struct ptr_valid_batch *batch)
170 return batch->child_pid != 0;
173 static void run_child(int infd, int outfd)
177 /* This is how we expect to exit. */
178 while (read(infd, &p, sizeof(p)) == sizeof(p)) {
184 if (read(infd, &size, sizeof(size)) != sizeof(size))
186 if (read(infd, &is_write, sizeof(is_write)) != sizeof(is_write))
189 for (i = 0; i < size; i++) {
195 /* If we're still here, the answer is "yes". */
196 if (write(outfd, &ret, 1) != 1)
202 static bool create_child(struct ptr_valid_batch *batch)
204 int outpipe[2], inpipe[2];
206 if (pipe(outpipe) != 0)
208 if (pipe(inpipe) != 0)
212 batch->child_pid = fork();
213 if (batch->child_pid == 0) {
216 run_child(outpipe[0], inpipe[1]);
219 if (batch->child_pid == -1)
225 batch->to_child = outpipe[1];
226 batch->from_child = inpipe[0];
230 batch->child_pid = 0;
231 close_noerr(inpipe[0]);
232 close_noerr(inpipe[1]);
234 close_noerr(outpipe[0]);
235 close_noerr(outpipe[1]);
239 static bool check_with_child(struct ptr_valid_batch *batch,
240 const void *p, size_t size, bool is_write)
244 if (!child_alive(batch)) {
245 if (!create_child(batch))
249 write(batch->to_child, &p, sizeof(p));
250 write(batch->to_child, &size, sizeof(size));
251 write(batch->to_child, &is_write, sizeof(is_write));
253 if (read(batch->from_child, &ret, sizeof(ret)) != sizeof(ret)) {
261 /* msync seems most well-defined test, but page could be mapped with
262 * no permissions, and can't distiguish readonly from writable. */
263 bool ptr_valid_batch(struct ptr_valid_batch *batch,
264 const void *p, size_t alignment, size_t size, bool write)
269 if ((intptr_t)p & (alignment - 1))
272 start = (void *)((intptr_t)p & ~(getpagesize() - 1));
273 end = (void *)(((intptr_t)p + size - 1) & ~(getpagesize() - 1));
275 /* We cache single page hits. */
277 if (batch->last && batch->last == start)
278 return batch->last_ok;
282 ret = check_with_maps(batch, p, size, write);
284 ret = check_with_child(batch, p, size, write);
288 batch->last_ok = ret;
294 bool ptr_valid_batch_string(struct ptr_valid_batch *batch, const char *p)
296 while (ptr_valid_batch(batch, p, 1, 1, false)) {
304 bool ptr_valid(const void *p, size_t alignment, size_t size, bool write)
307 struct ptr_valid_batch batch;
308 if (!ptr_valid_batch_start(&batch))
310 ret = ptr_valid_batch(&batch, p, alignment, size, write);
311 ptr_valid_batch_end(&batch);
315 bool ptr_valid_string(const char *p)
318 struct ptr_valid_batch batch;
319 if (!ptr_valid_batch_start(&batch))
321 ret = ptr_valid_batch_string(&batch, p);
322 ptr_valid_batch_end(&batch);
326 bool ptr_valid_batch_start(struct ptr_valid_batch *batch)
328 batch->child_pid = 0;
329 batch->maps = get_proc_maps(&batch->num_maps);
334 void ptr_valid_batch_end(struct ptr_valid_batch *batch)
336 if (child_alive(batch))