From e101026bdf2b8953378eb296dffb99f4e6c20aa8 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Mon, 17 Jul 2017 15:39:55 +0300 Subject: [PATCH] vdso: Don't miss rt-vvar while searching As rt-vvar can be placed lower (by address) than rt-vdso, it likely will go earlier in vma_area_list. That means that at the moment, when we've found rt-vdso, we already passed rt-vvar and rt_vvar_marked pointer will not be initialized. Search for rt-vvar during the second vma list traverse, so we will always find it if it's present. Signed-off-by: Dmitry Safonov Signed-off-by: Andrei Vagin --- criu/vdso.c | 121 +++++++++++++++++++++++++++------------------------- 1 file changed, 63 insertions(+), 58 deletions(-) diff --git a/criu/vdso.c b/criu/vdso.c index ffe97b95e..30c22aabd 100644 --- a/criu/vdso.c +++ b/criu/vdso.c @@ -106,9 +106,70 @@ static bool not_vvar_or_vdso(struct vma_area *vma) if (vma->e->flags & MAP_GROWSDOWN) return true; + BUILD_BUG_ON(!(VDSO_PROT & VVAR_PROT)); + if ((vma->e->prot & VVAR_PROT) != VVAR_PROT) + return true; + return false; } +static void drop_rt_vdso(struct vm_area_list *vma_area_list, + unsigned long orig_vdso_addr, unsigned long orig_vvar_addr, + unsigned long rt_vvar_addr, struct vma_area *rt_vdso_marked) +{ + struct vma_area *rt_vvar_marked = NULL; + struct vma_area *vma; + + if (!rt_vdso_marked) + return; + + /* + * There is marked vdso, it means such vdso is autogenerated + * and must be dropped from vma list. + */ + pr_debug("vdso: Found marked at %lx (orig vDSO at %lx VVAR at %lx)\n", + (long)rt_vdso_marked->e->start, orig_vdso_addr, orig_vvar_addr); + + /* + * Don't forget to restore the proxy vdso/vvar status, since + * they're unknown to the kernel. + * Also BTW search for rt-vvar to remove it later. + */ + list_for_each_entry(vma, &vma_area_list->h, list) { + if (vma->e->start == orig_vdso_addr) { + vma->e->status |= VMA_AREA_REGULAR | VMA_AREA_VDSO; + pr_debug("vdso: Restore orig vDSO status at %lx\n", + (long)vma->e->start); + } else if (vma->e->start == orig_vvar_addr) { + vma->e->status |= VMA_AREA_REGULAR | VMA_AREA_VVAR; + pr_debug("vdso: Restore orig VVAR status at %lx\n", + (long)vma->e->start); + } else if (rt_vvar_addr != VVAR_BAD_ADDR && + rt_vvar_addr == vma->e->start) { + BUG_ON(rt_vvar_marked); + if (not_vvar_or_vdso(vma)) { + pr_warn("Mark in rt-vdso points to vma, that doesn't look like vvar - skipping unmap\n"); + continue; + } + rt_vvar_marked = vma; + } + } + + pr_debug("vdso: Droppping marked vdso at %lx\n", + (long)rt_vdso_marked->e->start); + list_del(&rt_vdso_marked->list); + xfree(rt_vdso_marked); + vma_area_list->nr--; + + if (rt_vvar_marked) { + pr_debug("vdso: Droppping marked vvar at %lx\n", + (long)rt_vvar_marked->e->start); + list_del(&rt_vvar_marked->list); + xfree(rt_vvar_marked); + vma_area_list->nr--; + } +} + /* * The VMAs list might have proxy vdso/vvar areas left * from previous dump/restore cycle so we need to detect @@ -122,7 +183,6 @@ int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid, unsigned long orig_vvar_addr = VVAR_BAD_ADDR; unsigned long rt_vvar_addr = VVAR_BAD_ADDR; struct vma_area *rt_vdso_marked = NULL; - struct vma_area *rt_vvar_marked = NULL; struct parasite_vdso_vma_entry *args; int fd = -1, exit_code = -1; enum vdso_check_t vcheck; @@ -143,24 +203,6 @@ int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid, if (not_vvar_or_vdso(vma)) continue; - /* - * It might be possible VVAR area from marked - * vDSO zone, we need to detect it earlier than - * VDSO_PROT test because VVAR_PROT is a subset - * of it but don't yield continue here, - * sigh... what a mess. - */ - BUILD_BUG_ON(!(VDSO_PROT & VVAR_PROT)); - - if ((vma->e->prot & VVAR_PROT) == VVAR_PROT) { - if (rt_vvar_addr != VVAR_BAD_ADDR && - rt_vvar_addr == vma->e->start) { - BUG_ON(rt_vvar_marked); - rt_vvar_marked = vma; - continue; - } - } - if ((vma->e->prot & VDSO_PROT) != VDSO_PROT) continue; @@ -223,45 +265,8 @@ int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid, } } - /* - * There is marked vdso, it means such vdso is autogenerated - * and must be dropped from vma list. - */ - if (rt_vdso_marked) { - pr_debug("vdso: Found marked at %lx (orig vDSO at %lx VVAR at %lx)\n", - (long)rt_vdso_marked->e->start, - (long)orig_vdso_addr, (long)orig_vvar_addr); - - /* - * Don't forget to restore the proxy vdso/vvar status, since - * it's unknown to the kernel. - */ - list_for_each_entry(vma, &vma_area_list->h, list) { - if (vma->e->start == orig_vdso_addr) { - vma->e->status |= VMA_AREA_REGULAR | VMA_AREA_VDSO; - pr_debug("vdso: Restore orig vDSO status at %lx\n", - (long)vma->e->start); - } else if (vma->e->start == orig_vvar_addr) { - vma->e->status |= VMA_AREA_REGULAR | VMA_AREA_VVAR; - pr_debug("vdso: Restore orig VVAR status at %lx\n", - (long)vma->e->start); - } - } - - pr_debug("vdso: Droppping marked vdso at %lx\n", - (long)rt_vdso_marked->e->start); - list_del(&rt_vdso_marked->list); - xfree(rt_vdso_marked); - vma_area_list->nr--; - - if (rt_vvar_marked) { - pr_debug("vdso: Droppping marked vvar at %lx\n", - (long)rt_vvar_marked->e->start); - list_del(&rt_vvar_marked->list); - xfree(rt_vvar_marked); - vma_area_list->nr--; - } - } + drop_rt_vdso(vma_area_list, orig_vdso_addr, orig_vvar_addr, + rt_vvar_addr, rt_vdso_marked); exit_code = 0; err: close_safe(&fd);