criu/test/zdtm/static/vdso-proxy.c
Andrei Vagin a525b3c32e test/vdso-proxy: handle merged vma-s
When we compare two list of vma-s, we need to take into account that
some of them could be merged.

Fixes #12286

Signed-off-by: Andrei Vagin <avagin@google.com>
2025-11-13 08:40:46 -08:00

161 lines
3.8 KiB
C

#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include "zdtmtst.h"
const char *test_doc =
"Compare mappings before/after C/R for vdso/vvar presence. Should run iterative under vdso proxy fault-injection.\n";
const char *test_author = "Dmitry Safonov <dsafonov@virtuozzo.com>";
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)]))
#define VSYSCALL_START 0xffffffffff600000ULL
/*
* Use constant MAX_VMAS - to minimize the risk of allocating a new
* mapping or changing the size of existent VMA with realloc()
*/
#define MAX_VMAS 80
#define BUF_SIZE 1024
/*
* After C/R with vdso trampolines insertion, there should
* be added one or two vmas: vdso and possibly vvar.
* We need to check that nr. vmas after C/R <= +2 new vmas.
* Also previous vdso/vvar vma should still be present after C/R.
*/
struct vm_area {
/*
* Intentionally use 64bit integer to make sure that it's possible
* to parse mappings >4Gb - those might appear on ia32
* that's restored by x86_64 CRIU ¯\(°_o)/¯
*/
uint64_t start;
uint64_t end;
bool is_vvar_or_vdso;
};
static char buf[BUF_SIZE];
static int parse_maps(struct vm_area *vmas)
{
FILE *maps;
int i;
maps = fopen("/proc/self/maps", "r");
if (maps == NULL) {
pr_perror("Failed to open maps file");
return -1;
}
for (i = 0; i < MAX_VMAS; i++) {
struct vm_area *v = &vmas[i];
char *end;
if (fgets(buf, BUF_SIZE, maps) == NULL)
break;
v->start = strtoull(buf, &end, 16);
v->end = strtoull(end + 1, NULL, 16);
#if defined(__i386__)
/*
* XXX: ia32 is being restored from x86_64 and leaves
* emulated vsyscall "mapping". Hopefully, will be done
* per-process, ignore for now.
*/
if (v->start == VSYSCALL_START) {
i--;
continue;
}
#endif
v->is_vvar_or_vdso |= strstr(buf, "[vdso]") != NULL;
v->is_vvar_or_vdso |= strstr(buf, "[vvar]") != NULL;
v->is_vvar_or_vdso |= strstr(buf, "[vvar_vclock]") != NULL;
test_msg("[NOTE]\tVMA: [%#" PRIx64 ", %#" PRIx64 "]\n", v->start, v->end);
}
if (fclose(maps)) {
pr_perror("Failed to close maps file");
return -1;
}
if (i == MAX_VMAS) {
pr_err("Number of VMAs is bigger than reserved array's size\n");
return -1;
}
return i;
}
static int check_vvar_vdso(struct vm_area *before, int nr_before, struct vm_area *after, int nr_after)
{
int i, j = 0;
for (i = 0, j = 0; i < nr_before || j < nr_after;) {
if (j == nr_after || before[i].start < after[j].start) {
test_msg("[NOTE]\tLost mapping: %#" PRIx64 "-%#" PRIx64 "\n", before[i].start, before[i].end);
if (before[i].is_vvar_or_vdso) {
fail("Lost vvar/vdso mapping");
return -1;
}
i++;
continue;
}
if (i == nr_before || before[i].start > after[j].start) {
test_msg("[NOTE]\tNew mapping appeared: %#" PRIx64 "-%#" PRIx64 "\n", after[j].start, after[j].end);
j++;
continue;
}
if (before[i].end == after[j].end) {
i++;
j++;
} else if (before[i].end > after[j].end) {
before[i].start = after[j].end;
j++;
} else {
after[j].start = before[i].end;
i++;
}
}
return 0;
}
static struct vm_area vmas_before[MAX_VMAS];
static struct vm_area vmas_after[MAX_VMAS];
static int nr_before, nr_after;
int main(int argc, char *argv[])
{
test_init(argc, argv);
test_msg("[NOTE]\tMappings before:\n");
nr_before = parse_maps(vmas_before);
if (nr_before < 0) {
pr_err("Failed to parse maps\n");
return -1;
}
test_daemon();
test_waitsig();
test_msg("[NOTE]\tMappings after:\n");
nr_after = parse_maps(vmas_after);
if (nr_after < 0) {
pr_err("Failed to parse maps\n");
return -1;
}
/* After restore vDSO/VVAR blobs must remain in the old place. */
if (check_vvar_vdso(vmas_before, nr_before, vmas_after, nr_after))
return -1;
if (nr_before + 2 < nr_after) {
fail("There is more than two (VVAR/vDSO) vmas added after C/R");
return -1;
}
pass();
return 0;
}