On Fri, Apr 18, 2025 at 10:49:54AM -0700, Suren Baghdasaryan wrote: > Test that /proc/pid/maps does not report unexpected holes in the address > space when we concurrently remap a part of a vma into the middle of > another vma. This remapping results in the destination vma being split > into three parts and the part in the middle being patched back from, > all done concurrently from under the reader. We should always see either > original vma or the split one with no holes. > > Signed-off-by: Suren Baghdasaryan <sur...@google.com>
Umm, but we haven't fixed this in the mremap code right? :) So isn't this test failing right now? :P My understanding from meeting was you'd drop this commit/mark it skipped for now or something like this? > --- > tools/testing/selftests/proc/proc-pid-vm.c | 92 ++++++++++++++++++++++ > 1 file changed, 92 insertions(+) > > diff --git a/tools/testing/selftests/proc/proc-pid-vm.c > b/tools/testing/selftests/proc/proc-pid-vm.c > index 39842e4ec45f..1aef2db7e893 100644 > --- a/tools/testing/selftests/proc/proc-pid-vm.c > +++ b/tools/testing/selftests/proc/proc-pid-vm.c > @@ -663,6 +663,95 @@ static void test_maps_tearing_from_resize(int maps_fd, > 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); > +} > + > static int test_maps_tearing(void) > { > struct vma_modifier_info *mod_info; > @@ -757,6 +846,9 @@ static int test_maps_tearing(void) > 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); > -- > 2.49.0.805.g082f7c87e0-goog >