signal_state(mod_info, TEST_DONE);
}
+static inline void remap_vma(const struct vma_modifier_info *mod_info)
+{
+ /*
+ * Remap the last page of the next vma into the middle of the vma.
+ * This splits the current vma and the first and middle parts (the
+ * parts at lower addresses) become the last vma objserved in the
+ * first page and the first vma observed in the last page.
+ */
+ assert(mremap(mod_info->next_addr + page_size * 2, page_size,
+ page_size, MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP,
+ mod_info->addr + page_size) != MAP_FAILED);
+}
+
+static inline void patch_vma(const struct vma_modifier_info *mod_info)
+{
+ assert(!mprotect(mod_info->addr + page_size, page_size,
+ mod_info->prot));
+}
+
+static inline void check_remap_result(struct line_content *mod_last_line,
+ struct line_content *mod_first_line,
+ struct line_content *restored_last_line,
+ struct line_content *restored_first_line)
+{
+ /* Make sure vmas at the boundaries are changing */
+ assert(strcmp(mod_last_line->text, restored_last_line->text) != 0);
+ assert(strcmp(mod_first_line->text, restored_first_line->text) != 0);
+}
+
+static void test_maps_tearing_from_remap(int maps_fd,
+ struct vma_modifier_info *mod_info,
+ struct page_content *page1,
+ struct page_content *page2,
+ struct line_content *last_line,
+ struct line_content *first_line)
+{
+ struct line_content remapped_last_line;
+ struct line_content remapped_first_line;
+ struct line_content restored_last_line;
+ struct line_content restored_first_line;
+
+ wait_for_state(mod_info, SETUP_READY);
+
+ /* re-read the file to avoid using stale data from previous test */
+ read_boundary_lines(maps_fd, page1, page2, last_line, first_line);
+
+ mod_info->vma_modify = remap_vma;
+ mod_info->vma_restore = patch_vma;
+ mod_info->vma_mod_check = check_remap_result;
+
+ capture_mod_pattern(maps_fd, mod_info, page1, page2, last_line, first_line,
+ &remapped_last_line, &remapped_first_line,
+ &restored_last_line, &restored_first_line);
+
+ /* Now start concurrent modifications for test_duration_sec */
+ signal_state(mod_info, TEST_READY);
+
+ struct line_content new_last_line;
+ struct line_content new_first_line;
+ struct timespec start_ts, end_ts;
+
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &start_ts);
+ do {
+ read_boundary_lines(maps_fd, page1, page2, &new_last_line, &new_first_line);
+
+ /* Check if we read vmas after remapping it */
+ if (!strcmp(new_last_line.text, remapped_last_line.text)) {
+ /*
+ * The vmas should be consistent with remap results,
+ * however if the vma was concurrently restored, it
+ * can be reported twice (first as split one, then
+ * as restored one) because we found it as the next vma
+ * again. In that case new first line will be the same
+ * as the last restored line.
+ */
+ assert(!strcmp(new_first_line.text, remapped_first_line.text) ||
+ !strcmp(new_first_line.text, restored_last_line.text));
+ } else {
+ /* The vmas should be consistent with the original/resored state */
+ assert(!strcmp(new_last_line.text, restored_last_line.text) &&
+ !strcmp(new_first_line.text, restored_first_line.text));
+ }
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &end_ts);
+ } while (end_ts.tv_sec - start_ts.tv_sec < test_duration_sec);
+
+ /* Signal the modifyer thread to stop and wait until it exits */
+ signal_state(mod_info, TEST_DONE);
+}
+
int usage(void)
{
fprintf(stderr, "Userland /proc/pid/{s}maps race test cases\n");
test_maps_tearing_from_resize(maps_fd, mod_info, &page1, &page2,
&last_line, &first_line);
+ test_maps_tearing_from_remap(maps_fd, mod_info, &page1, &page2,
+ &last_line, &first_line);
+
stop_vma_modifier(mod_info);
free(page2.data);