summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/proc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/proc')
-rw-r--r--tools/testing/selftests/proc/proc-maps-race.c296
-rw-r--r--tools/testing/selftests/proc/proc-pidns.c1
2 files changed, 234 insertions, 63 deletions
diff --git a/tools/testing/selftests/proc/proc-maps-race.c b/tools/testing/selftests/proc/proc-maps-race.c
index a734553718da..415eccb70468 100644
--- a/tools/testing/selftests/proc/proc-maps-race.c
+++ b/tools/testing/selftests/proc/proc-maps-race.c
@@ -17,8 +17,8 @@
*/
/*
* Fork a child that concurrently modifies address space while the main
- * process is reading /proc/$PID/maps and verifying the results. Address
- * space modifications include:
+ * process is reading /proc/$PID/maps and /proc/$PID/smaps, verifying the
+ * results. Address space modifications include:
* VMA splitting and merging
*
*/
@@ -39,6 +39,13 @@
#include <sys/types.h>
#include <sys/wait.h>
+#define min(a, b) \
+ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a < _b ? _a : _b; \
+ })
+
/* /proc/pid/maps parsing routines */
struct page_content {
char *data;
@@ -66,6 +73,11 @@ enum test_state {
TEST_DONE,
};
+enum maps_file {
+ MAPS,
+ SMAPS,
+};
+
struct vma_modifier_info;
FIXTURE(proc_maps_race)
@@ -76,7 +88,9 @@ FIXTURE(proc_maps_race)
struct line_content last_line;
struct line_content first_line;
unsigned long duration_sec;
+ enum maps_file maps_file;
int shared_mem_size;
+ int skip_pages;
int page_size;
int vma_count;
bool verbose;
@@ -84,6 +98,19 @@ FIXTURE(proc_maps_race)
pid_t pid;
};
+FIXTURE_VARIANT(proc_maps_race)
+{
+ const enum maps_file maps_file;
+};
+
+FIXTURE_VARIANT_ADD(proc_maps_race, maps) {
+ .maps_file = MAPS,
+};
+
+FIXTURE_VARIANT_ADD(proc_maps_race, smaps) {
+ .maps_file = SMAPS,
+};
+
typedef bool (*vma_modifier_op)(FIXTURE_DATA(proc_maps_race) *self);
typedef bool (*vma_mod_result_check_op)(struct line_content *mod_last_line,
struct line_content *mod_first_line,
@@ -105,38 +132,102 @@ struct vma_modifier_info {
void *child_mapped_addr[];
};
-
-static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self)
+static bool read_page(FIXTURE_DATA(proc_maps_race) *self,
+ struct page_content *page)
{
ssize_t bytes_read;
- if (lseek(self->maps_fd, 0, SEEK_SET) < 0)
+ bytes_read = read(self->maps_fd, page->data, self->page_size);
+ if (bytes_read <= 0)
return false;
- bytes_read = read(self->maps_fd, self->page1.data, self->page_size);
- if (bytes_read <= 0)
+ /* Make sure data always ends with a newline character. */
+ if (page->data[bytes_read - 1] != '\n')
return false;
- self->page1.size = bytes_read;
+ page->size = bytes_read;
- bytes_read = read(self->maps_fd, self->page2.data, self->page_size);
- if (bytes_read <= 0)
+ return true;
+}
+
+static bool parse_vma_line(char *line_start, char *line_end,
+ unsigned long *start, unsigned long *end)
+{
+ bool found;
+
+ *line_end = '\0'; /* stop sscanf at the EOL */
+ found = (sscanf(line_start, "%lx-%lx", start, end) == 2);
+ *line_end = '\n';
+
+ return found;
+}
+
+static int locate_containing_page(FIXTURE_DATA(proc_maps_race) *self,
+ unsigned long addr, unsigned long size)
+{
+ unsigned long start, end;
+ int page = 0;
+
+ if (lseek(self->maps_fd, 0, SEEK_SET) < 0)
+ return -1;
+
+ while (true) {
+ char *curr_pos;
+ char *end_pos;
+
+ if (!read_page(self, &self->page1))
+ return -1;
+
+ curr_pos = self->page1.data;
+ end_pos = self->page1.data + self->page1.size;
+ while (curr_pos < end_pos) {
+ char *line_end;
+
+ line_end = strchr(curr_pos, '\n');
+ if (!line_end)
+ break;
+
+ if (parse_vma_line(curr_pos, line_end, &start, &end) &&
+ start == addr && end == addr + size)
+ return page;
+
+ curr_pos = line_end + 1;
+ }
+ page++;
+ }
+
+ return 0;
+}
+
+static bool read_two_pages(FIXTURE_DATA(proc_maps_race) *self)
+{
+ if (lseek(self->maps_fd, 0, SEEK_SET) < 0)
return false;
- self->page2.size = bytes_read;
+ for (int i = 0; i < self->skip_pages; i++)
+ if (!read_page(self, &self->page1))
+ return false;
- return true;
+ return read_page(self, &self->page1) && read_page(self, &self->page2);
}
-static void copy_first_line(struct page_content *page, char *first_line)
+static void copy_line(const char *line_start, const char *line_end,
+ char *buf, size_t buf_size)
{
- char *pos = strchr(page->data, '\n');
+ size_t len = min(line_end - line_start, buf_size - 1);
- strncpy(first_line, page->data, pos - page->data);
- first_line[pos - page->data] = '\0';
+ strncpy(buf, line_start, len);
+ buf[len] = '\0';
}
-static void copy_last_line(struct page_content *page, char *last_line)
+static void copy_first_line(struct page_content *page, char *first_line,
+ size_t line_size)
+{
+ copy_line(page->data, strchr(page->data, '\n'), first_line, line_size);
+}
+
+static void copy_last_line(struct page_content *page, char *last_line,
+ size_t line_size)
{
/* Get the last line in the first page */
const char *end = page->data + page->size - 1;
@@ -146,8 +237,59 @@ static void copy_last_line(struct page_content *page, char *last_line)
/* search previous newline */
while (pos[-1] != '\n')
pos--;
- strncpy(last_line, pos, end - pos);
- last_line[end - pos] = '\0';
+
+ copy_line(pos, end, last_line, line_size);
+}
+
+static bool copy_first_entry(struct page_content *page, char *first_line,
+ size_t line_size)
+{
+ char *start_pos = page->data;
+
+ while (start_pos < page->data + page->size) {
+ unsigned long start_addr;
+ unsigned long end_addr;
+ char *end_pos;
+
+ end_pos = strchr(start_pos, '\n');
+ if (!end_pos)
+ break;
+
+ if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) {
+ copy_line(start_pos, end_pos, first_line, line_size);
+ return true;
+ }
+
+ start_pos = end_pos + 1;
+ }
+
+ return false;
+}
+
+static bool copy_last_entry(struct page_content *page, char *last_line,
+ size_t line_size)
+{
+ char *end_pos = page->data + page->size - 1;
+ char *start_pos;
+
+ while (end_pos > page->data) {
+ unsigned long start_addr;
+ unsigned long end_addr;
+
+ /* skip last newline */
+ start_pos = end_pos - 1;
+ /* search previous newline */
+ while (start_pos > page->data && start_pos[-1] != '\n')
+ start_pos--;
+ if (parse_vma_line(start_pos, end_pos, &start_addr, &end_addr)) {
+ copy_line(start_pos, end_pos, last_line, line_size);
+ return true;
+ }
+
+ end_pos = start_pos - 1;
+ }
+
+ return false;
}
/* Read the last line of the first page and the first line of the second page */
@@ -158,8 +300,16 @@ static bool read_boundary_lines(FIXTURE_DATA(proc_maps_race) *self,
if (!read_two_pages(self))
return false;
- copy_last_line(&self->page1, last_line->text);
- copy_first_line(&self->page2, first_line->text);
+ if (self->maps_file == MAPS) {
+ copy_last_line(&self->page1, last_line->text, LINE_MAX_SIZE);
+ copy_first_line(&self->page2, first_line->text, LINE_MAX_SIZE);
+ } else if (self->maps_file == SMAPS) {
+ if (!copy_last_entry(&self->page1, last_line->text, LINE_MAX_SIZE) ||
+ !copy_first_entry(&self->page2, first_line->text, LINE_MAX_SIZE))
+ return false;
+ } else {
+ return false;
+ }
return sscanf(last_line->text, "%lx-%lx", &last_line->start_addr,
&last_line->end_addr) == 2 &&
@@ -340,7 +490,8 @@ static bool query_addr_at(int maps_fd, void *addr,
static inline bool split_vma(FIXTURE_DATA(proc_maps_race) *self)
{
- return mmap(self->mod_info->addr, self->page_size, self->mod_info->prot | PROT_EXEC,
+ /* PROT_NONE differs from both readable neighbors. */
+ return mmap(self->mod_info->addr, self->page_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED;
}
@@ -418,11 +569,14 @@ FIXTURE_SETUP(proc_maps_race)
struct vma_modifier_info *mod_info;
pthread_mutexattr_t mutex_attr;
pthread_condattr_t cond_attr;
+ unsigned long first_map_addr;
+ unsigned long last_map_addr;
unsigned long duration_sec;
char fname[32];
self->page_size = (unsigned long)sysconf(_SC_PAGESIZE);
self->verbose = verbose && !strncmp(verbose, "1", 1);
+ self->maps_file = variant->maps_file;
duration_sec = duration ? atol(duration) : 0;
self->duration_sec = duration_sec ? duration_sec : 5UL;
@@ -489,7 +643,16 @@ FIXTURE_SETUP(proc_maps_race)
exit(0);
}
- sprintf(fname, "/proc/%d/maps", self->pid);
+ switch (self->maps_file) {
+ case MAPS:
+ sprintf(fname, "/proc/%d/maps", self->pid);
+ break;
+ case SMAPS:
+ sprintf(fname, "/proc/%d/smaps", self->pid);
+ break;
+ default:
+ ksft_exit_fail();
+ }
self->maps_fd = open(fname, O_RDONLY);
ASSERT_NE(self->maps_fd, -1);
@@ -502,6 +665,13 @@ FIXTURE_SETUP(proc_maps_race)
self->page2.data = malloc(self->page_size);
ASSERT_NE(self->page2.data, NULL);
+ first_map_addr = (unsigned long)mod_info->child_mapped_addr[0];
+ last_map_addr = (unsigned long)mod_info->child_mapped_addr[mod_info->vma_count - 1];
+
+ self->skip_pages = locate_containing_page(self,
+ min(first_map_addr, last_map_addr),
+ self->page_size * 3);
+ ASSERT_NE(self->skip_pages, -1);
ASSERT_TRUE(read_boundary_lines(self, &self->last_line, &self->first_line));
/*
@@ -527,7 +697,6 @@ FIXTURE_SETUP(proc_maps_race)
ASSERT_TRUE(mod_info->addr && mod_info->next_addr);
signal_state(mod_info, PARENT_READY);
-
}
FIXTURE_TEARDOWN(proc_maps_race)
@@ -617,20 +786,20 @@ TEST_F(proc_maps_race, test_maps_tearing_from_split)
last_line_changed = strcmp(new_last_line.text, self->last_line.text) != 0;
first_line_changed = strcmp(new_first_line.text, self->first_line.text) != 0;
ASSERT_EQ(last_line_changed, first_line_changed);
-
- /* Check if PROCMAP_QUERY ioclt() finds the right VMA */
- ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
- &vma_start, &vma_end));
- /*
- * The vma at the split address can be either the same as
- * original one (if read before the split) or the same as the
- * first line in the second page (if read after the split).
- */
- ASSERT_TRUE((vma_start == self->last_line.start_addr &&
- vma_end == self->last_line.end_addr) ||
- (vma_start == split_first_line.start_addr &&
- vma_end == split_first_line.end_addr));
-
+ if (self->maps_file == MAPS) {
+ /* Check if PROCMAP_QUERY ioclt() finds the right VMA */
+ ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
+ &vma_start, &vma_end));
+ /*
+ * The vma at the split address can be either the same as
+ * original one (if read before the split) or the same as the
+ * first line in the second page (if read after the split).
+ */
+ ASSERT_TRUE((vma_start == self->last_line.start_addr &&
+ vma_end == self->last_line.end_addr) ||
+ (vma_start == split_first_line.start_addr &&
+ vma_end == split_first_line.end_addr));
+ }
clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
end_test_iteration(&end_ts, self->verbose);
} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
@@ -700,17 +869,18 @@ TEST_F(proc_maps_race, test_maps_tearing_from_resize)
strcmp(new_first_line.text, restored_first_line.text),
"Expand result invalid", self));
}
-
- /* Check if PROCMAP_QUERY ioclt() finds the right VMA */
- ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr, &vma_start, &vma_end));
- /*
- * The vma should stay at the same address and have either the
- * original size of 3 pages or 1 page if read after shrinking.
- */
- ASSERT_TRUE(vma_start == self->last_line.start_addr &&
- (vma_end - vma_start == self->page_size * 3 ||
- vma_end - vma_start == self->page_size));
-
+ if (self->maps_file == MAPS) {
+ /* Check if PROCMAP_QUERY ioclt() finds the right VMA */
+ ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr,
+ &vma_start, &vma_end));
+ /*
+ * The vma should stay at the same address and have either the
+ * original size of 3 pages or 1 page if read after shrinking.
+ */
+ ASSERT_TRUE(vma_start == self->last_line.start_addr &&
+ (vma_end - vma_start == self->page_size * 3 ||
+ vma_end - vma_start == self->page_size));
+ }
clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
end_test_iteration(&end_ts, self->verbose);
} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
@@ -780,20 +950,20 @@ TEST_F(proc_maps_race, test_maps_tearing_from_remap)
strcmp(new_first_line.text, restored_first_line.text),
"Remap restore result invalid", self));
}
-
- /* Check if PROCMAP_QUERY ioclt() finds the right VMA */
- ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
- &vma_start, &vma_end));
- /*
- * The vma should either stay at the same address and have the
- * original size of 3 pages or we should find the remapped vma
- * at the remap destination address with size of 1 page.
- */
- ASSERT_TRUE((vma_start == self->last_line.start_addr &&
- vma_end - vma_start == self->page_size * 3) ||
- (vma_start == self->last_line.start_addr + self->page_size &&
- vma_end - vma_start == self->page_size));
-
+ if (self->maps_file == MAPS) {
+ /* Check if PROCMAP_QUERY ioclt() finds the right VMA */
+ ASSERT_TRUE(query_addr_at(self->maps_fd, mod_info->addr + self->page_size,
+ &vma_start, &vma_end));
+ /*
+ * The vma should either stay at the same address and have the
+ * original size of 3 pages or we should find the remapped vma
+ * at the remap destination address with size of 1 page.
+ */
+ ASSERT_TRUE((vma_start == self->last_line.start_addr &&
+ vma_end - vma_start == self->page_size * 3) ||
+ (vma_start == self->last_line.start_addr + self->page_size &&
+ vma_end - vma_start == self->page_size));
+ }
clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
end_test_iteration(&end_ts, self->verbose);
} while (end_ts.tv_sec - start_ts.tv_sec < self->duration_sec);
diff --git a/tools/testing/selftests/proc/proc-pidns.c b/tools/testing/selftests/proc/proc-pidns.c
index 25b9a2933c45..6f7c10fe97b3 100644
--- a/tools/testing/selftests/proc/proc-pidns.c
+++ b/tools/testing/selftests/proc/proc-pidns.c
@@ -6,6 +6,7 @@
#include <assert.h>
#include <errno.h>
+#include <fcntl.h>
#include <sched.h>
#include <stdbool.h>
#include <stdlib.h>