From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail.openvz.org (unknown [69.168.225.77]) by lore.virtuozzo.com (Postfix) with ESMTPS id EC4FB802A5 for ; Wed, 14 Oct 2026 17:16:56 +0000 (UTC) Received: from mail.openvz.org (localhost [127.0.0.1]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69EHFgS8001563; Wed, 14 Oct 2026 20:15:44 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69EHFgS8001563 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="lXb8hy97" Received: from mail-wr1-f71.google.com (mail-wr1-f71.google.com [209.85.221.71]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69EHF8ci001483 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Wed, 14 Oct 2026 20:15:08 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69EHF8ci001483 Received: by mail-wr1-f71.google.com with SMTP id ffacd0b85a97d-48b09f01f56so2787286f8f.1 for ; Wed, 14 Oct 2026 10:15:08 -0700 (PDT) X-Gm-Message-State: AFq9FYLohzQPkv5pNX2iZ8M7GwGXSpnp5qxq62FiTgV60xIZcrY84dV5 Izc1/zuTp49hAea0bzwUzsOcHd+ZwNYJtEk2bgIaTH8I0ihvDAU/UaCmupkEB2nYFADpEZOt9Ws 3lRY+k6htOvicxQ10iFWSJidYhm2II0VfqT1HzEqJpOtobS/X0MIdyg== X-Gm-Gg: AYBFou00LqE8pzT5dkRr7UrmDl+L2GxliQdc1z4iWlN9tRzuVFIoT+QK7OE95/rXgO4 qZz1Eaft5bSZVPfubQIwcbeX0+k9xOYqRyKBnjOsN7qKDRFjVLKrgkd8jUA3NrdIhdHuJs6AZSu 3oSYvs1Jo8i0vKA4Cj/KmJ8VWD05eY+rr8dGpqnfybnQjS1QW6GmMJ5qdvht57svOH0Y2XL8WAk MFQEbQ7A/OgfqgWyBIInG/J4oGReUJOe7W9OBMsXRNktKK/oNiuzfE2FEH11F3Rhx+hl0hscBS2 CTq8R2hdcfw2ueDSyEW2bEAw13QM47hD7rwlYTHT9ykEFgeLawhxMcG/gP+q37XOinVDtbqRU// kJSRQzAqAccmesDPt X-Received: by 2002:adf:e19a:0:b0:486:f6dc:52c8 with SMTP id ffacd0b85a97d-48dbace511cmr29358642f8f.37.1791998108288; Wed, 14 Oct 2026 10:15:08 -0700 (PDT) X-Received: by 2002:adf:e19a:0:b0:486:f6dc:52c8 with SMTP id ffacd0b85a97d-48dbace511cmr29358475f8f.37.1791998106291; Wed, 14 Oct 2026 10:15:06 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1791998106; cv=none; d=google.com; s=arc-20260327; b=A16sG2tfhpKJTtEHDZ+tyrGs8eztzFsOnw/liQ9gcl2UQzmLTwbZI71telQaVbvh1F oxANvmutJwfAoMEGM+qqLzbRnAu2M5lyfffovAgAHlm4rHUuvG3CSx0ClKZxNTuVXpJ7 0i1/RamOkVbbobiLKaSkpE7bTcRCO3xcQOnWtPmmbKVx7PEej+o3XKvQOUT1HUGp2TDN N/vs4RzRi7+udphd96mbaFWHJ1PBK7ueXRmW9IC2j6EopI6f4Dr/beGk4+20R/UPqpNL CH3P8Nz7y+viOB/tkQ0aDG8icsdrhurSd2s3SrUBVe8bh8T81+xQJpVyYFFR4EQL4Rht x02g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:dkim-signature; bh=pSCllcYYmo631el/Xb0bnuZgYU8+LQi1ZK+u/5t6aIk=; fh=xUcS46XhSaUOWFT7CPWNuABJ4EJ7/+S5u4N+e+rlWN4=; b=FOplYlr2ZXeBYnbj+4bNI4nZDhlRFxteYBgoAEd0HD4uLLGwE0NqLTV7lriF7x5Td7 fV7BWYwDEQQsGfS+SECy7SuRZgYK64F3rWhZD2ZGa2sdukyLA0WKdPOlzSpTt+YadfIr +sWMZaQtlZV+JkL2Euzvy1ns4mvEacohlXZ8RZuP4UMjPScJYQSdDy9q/xw27Nfxudqo RxchPuLFBHJ8Ko1EhLOk+3VCRCGrm1SKcCZQzj03/pnXwDPgNpqV+svI2mTdTWe2mIm5 OX4QkvReTMT0P84V5gT3ncWeO2rs5wuP/Zs+IWepmKeIQe4aXfIMYc38GnoggiK3SEAZ auTg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=lXb8hy97; spf=pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=khorenko@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com Received: from relay.virtuozzo.com (relay.virtuozzo.com. [130.117.225.111]) by mx.google.com with ESMTPS id ffacd0b85a97d-48df065a824si2006188f8f.332.2026.10.14.10.15.06 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 14 Oct 2026 10:15:06 -0700 (PDT) Received-SPF: pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) client-ip=130.117.225.111; Authentication-Results: mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=lXb8hy97; spf=pass (google.com: domain of khorenko@virtuozzo.com designates 130.117.225.111 as permitted sender) smtp.mailfrom=khorenko@virtuozzo.com; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=virtuozzo.com DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=Message-Id:Content-Type:MIME-Version:Subject:Date :From; bh=pSCllcYYmo631el/Xb0bnuZgYU8+LQi1ZK+u/5t6aIk=; b=lXb8hy97hxNeshbzPnP zbomYGlU6k41gdqF5rvvy47sgaO+bGOIdOYKt2F/9T0lNVlCqcvK93PDO3y8y1OgHS1mti9vKNADF HQn4lZcaCFFAaZP0G3uoyprMsVJftmzT3s33812Kld2suVgDYJc3sLV6fMmvYMCxbKw5Fm94Lk/ZT ZERVcvFVBZiK2AuVtxqj4CXd+IyR+nGYFPdIWYOwm0o5NOUWGbd4SaoxmtY2MKyYwQ5FmC/9q72L4 /ZtvXo/3tnPuIfddqAOoCSx20BW5Xm/bSSEPuXosva8H1/7FrvAY0zTY/M6dyPcWDrR9NsPEUubIY So5j9WY4wpr8cqw==; Received: from ch-demo-asa.virtuozzo.com ([130.117.225.8] helo=f0.sw.ru) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1xH2Vo-008657-2O; Wed, 14 Oct 2026 19:15:04 +0200 From: Konstantin Khorenko Date: Wed, 14 Oct 2026 19:15:05 +0200 MIME-Version: 1.0 Message-Id: <20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-7-b23b454691db@virtuozzo.com> References: <20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-0-b23b454691db@virtuozzo.com> In-Reply-To: <20261014-b4-prep-khorenko-vstor-149847-5-doexec-selftests-v1-0-b23b454691db@virtuozzo.com> To: Pavel Tikhomirov , Andrey Drobyshev , Vasileios Almpanis X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=24967; i=khorenko@virtuozzo.com; h=from:subject:message-id; bh=rwKtivznDl9vKtgIEDu7fpGMKsu+JY3MsDmFUznvoLQ=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBqz7iXh3BYq9ZIty9GrnrHxbf5cJMdJO3oyMVve qDG+IDHuXeJAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCas+4lwAKCRBRlgpH+WFc KjVVD/0ZweoTD7oMudEHHmipu1b1b0CYlkA4htN3urMFUKiDjobJW/dWK8fg44Qi4WQAxqs2xLW gJ3FU9DB64rUUjXAPI9sXFU5naKOKMiWPTMnszPJpU7v8ssDxPJp6KjhThdqtvK75S+J8lfiQx/ Va+vyJrxndZmP8BK+QozYsr1Nbz7ZP6BbWq57wOgeI+OQ14P5SMVefZj5BfZUJKvngZblrj3jlt 9ly8M2xX0Yc+Eou2ampyMJ7f4y8BLnVJds+0KC3mAQkoG4r1liI0LsBmyM3usA2SAhwrjcT5a/R 0CYSqkYzRY0RHpirjOcQ0gaph81oEClkNwBV0BuHxYBetOzpUMZpBT1f5MAt6Xg5BScfqDp2K0F EQA+lFCS9hEDFM2dYFN1L4djhDhcLy7+ezj6KhopjHSwNXgfuZiF8RkdYXXEVYEoZmnjXoXHts+ 8EPG8OGI9DLGBrJVftq9Kt/ZYAynqdoQKw2NtSa9RJ2XDgc0DsWlRLgjY3g+ZbAFryfKrr7Swzt Oh8s7gLayeo4HqXJ3rB7rO+Aygzhlo5qVpPuC2lv8arNAitpVUIC9yXU4D6CF+m7GNpOTkhJLnI 3Yw3eZlD5PDvSb4u6JyMJ2eG1bqS/YwMh2nB+SgGFPIrRSUyf927e5bDGOGTQpR3mp1QjdocZ0r L1J3YeQ66+baOVA== X-Developer-Key: i=khorenko@virtuozzo.com; a=openpgp; fpr=560F6DC83DC5D3D063BD082C51960A47F9615C2A X-OZ-Fwd: true Cc: devel@openvz.org Subject: [Devel] [PATCH hci-8.1 7/7] selftests/mm: say why a doexec case fails X-BeenThere: devel@openvz.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: OpenVZ development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: devel-bounces@openvz.org Errors-To: devel-bounces@openvz.org Many failure paths of doexec return KSFT_FAIL without a word, so a failing case shows nothing but "not ok". Print the call that failed with its errno, or what the check found, before failing. https://virtuozzo.atlassian.net/browse/VSTOR-150234 Feature: oracle/mm: MADV_DOEXEC madvise() flag Assisted-by: LLM Signed-off-by: Konstantin Khorenko --- tools/testing/selftests/mm/doexec.c | 223 ++++++++++++++++++++++-------------- 1 file changed, 136 insertions(+), 87 deletions(-) diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c index 1b33bc489eb71..f74471abe13e2 100644 --- a/tools/testing/selftests/mm/doexec.c +++ b/tools/testing/selftests/mm/doexec.c @@ -103,6 +103,8 @@ static int wait_child(pid_t pid) do { ret = waitpid(pid, &status, 0); } while (ret < 0 && errno == EINTR); + if (ret >= 0 && WIFSIGNALED(status)) + ksft_print_msg("child killed by signal %d\n", WTERMSIG(status)); if (ret < 0 || !WIFEXITED(status)) return KSFT_FAIL; return WEXITSTATUS(status); @@ -171,6 +173,13 @@ static int fail(const char *what) return KSFT_FAIL; } +static int fail_errno(const char *what) +{ + ksft_print_msg("%s: %s\n", what, strerror(errno)); + fflush(stdout); + return KSFT_FAIL; +} + static int check_contents(char *ram, size_t size, unsigned char value) { return check_bytes(ram, size, value) ? fail("contents lost") : KSFT_PASS; @@ -179,14 +188,14 @@ static int check_contents(char *ram, size_t size, unsigned char value) static int after_cow_exec(char *ram, size_t size, pid_t sibling, int pipe_fd) { char ready = 1; - int ret = check_bytes(ram, size, 0x31); + int ret = check_bytes(ram, size, 0x31) ? fail("contents lost") : KSFT_PASS; memset(ram, 0x42, size); if (write(pipe_fd, &ready, 1) != 1) - ret = KSFT_FAIL; + ret = fail("lost the COW sibling"); close(pipe_fd); if (wait_child(sibling)) - ret = KSFT_FAIL; + ret = fail("COW sibling saw the new image's writes"); return ret; } @@ -202,20 +211,22 @@ static int before_cow_exec(void) ram = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (ram == MAP_FAILED) - return KSFT_FAIL; + return fail_errno("mmap"); memset(ram, 0x31, size); if (madvise(ram, size, MADV_DOEXEC)) return unsupported(); if (pipe(pipe_fd)) - return KSFT_FAIL; + return fail_errno("pipe"); pid = fork(); if (pid < 0) - return KSFT_FAIL; + return fail_errno("fork"); if (!pid) { close(pipe_fd[1]); if (read(pipe_fd[0], &ready, 1) != 1) _exit(KSFT_FAIL); - _exit(check_bytes(ram, size, 0x31)); + if (check_bytes(ram, size, 0x31)) + _exit(fail("COW sibling lost its contents")); + _exit(KSFT_PASS); } close(pipe_fd[0]); snprintf(addr_arg, sizeof(addr_arg), "%llu", @@ -225,6 +236,7 @@ static int before_cow_exec(void) snprintf(fd_arg, sizeof(fd_arg), "%d", pipe_fd[1]); execl("/proc/self/exe", "doexec", "--cow", addr_arg, size_arg, pid_arg, fd_arg, NULL); + fail_errno("exec"); close(pipe_fd[1]); wait_child(pid); return KSFT_FAIL; @@ -242,8 +254,7 @@ static void exec_stage(int fd, char *ram, size_t size, int kind, int stage) snprintf(stage_arg, sizeof(stage_arg), "%d", stage); execl("/proc/self/exe", "doexec", "--exec", fd_arg, addr_arg, size_arg, kind_arg, stage_arg, NULL); - ksft_print_msg("exec: %s\n", strerror(errno)); - _exit(KSFT_FAIL); + _exit(fail_errno("exec")); } static int after_exec(int fd, char *ram, size_t size, int kind, int stage) @@ -255,33 +266,35 @@ static int after_exec(int fd, char *ram, size_t size, int kind, int stage) if (check_pin(fd, ram, size, before)) return KSFT_FAIL; if (kind == PROTECTED && mprotect(ram, size, PROT_READ | PROT_WRITE)) - return KSFT_FAIL; + return fail_errno("mprotect"); memset(ram, after, size); if (check_pin(fd, ram, size, after)) return KSFT_FAIL; if (stage == 1) { /* VM_EXEC_KEEP is consumed, so mark again for the second exec. */ if (madvise(ram, size, MADV_DOEXEC)) - return KSFT_FAIL; + return fail_errno("MADV_DOEXEC"); if (kind == PROTECTED && mprotect(ram, size, PROT_READ)) - return KSFT_FAIL; + return fail_errno("mprotect"); exec_stage(fd, ram, size, kind, 2); } /* Fork must copy into the child and retain the parent's pinned pages. */ pid = fork(); if (pid < 0) - return KSFT_FAIL; + return fail_errno("fork"); if (!pid) { memset(ram, 0x64, size); _exit(KSFT_PASS); } - if (wait_child(pid) || check_pin(fd, ram, size, after)) + if (wait_child(pid)) + return fail("fork child of the new image failed"); + if (check_pin(fd, ram, size, after)) return KSFT_FAIL; memset(ram, 0x75, size); if (check_pin(fd, ram, size, 0x75)) return KSFT_FAIL; - return ioctl(fd, PIN_LONGTERM_TEST_STOP) ? KSFT_FAIL : KSFT_PASS; + return ioctl(fd, PIN_LONGTERM_TEST_STOP) ? fail_errno("PIN_LONGTERM_TEST_STOP") : KSFT_PASS; } static size_t huge_page_size(void) @@ -316,7 +329,7 @@ static int before_exec(int kind) mapping = mmap(NULL, 2 * size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mapping == MAP_FAILED) - return KSFT_FAIL; + return fail_errno("mmap"); aligned = ((uintptr_t)mapping + size - 1) & ~(size - 1); ram = (char *)aligned; if (ram != mapping) @@ -325,7 +338,7 @@ static int before_exec(int kind) munmap(ram + size, mapping + 2 * size - (ram + size)); if (madvise(ram, size, kind == HUGE ? MADV_HUGEPAGE : MADV_NOHUGEPAGE) && (kind == HUGE || errno != EINVAL)) - return KSFT_FAIL; + return fail_errno("THP madvise"); memset(ram, 0x31, size); if (kind == HUGE && madvise(ram, size, MADV_COLLAPSE)) return KSFT_SKIP; @@ -345,7 +358,7 @@ static int before_exec(int kind) return errno == ENOTTY ? KSFT_SKIP : KSFT_FAIL; } if (kind == PROTECTED && mprotect(ram, size, PROT_READ)) - return KSFT_FAIL; + return fail_errno("mprotect"); exec_stage(fd, ram, size, kind, 1); return KSFT_FAIL; } @@ -366,7 +379,13 @@ static int exec_path(const char *exe, char *ram, size_t size, size_t stride, static int check_exec(char *ram, size_t size, size_t stride, bool reject) { - return exec_path("/proc/self/exe", ram, size, stride, reject); + int err = exec_path("/proc/self/exe", ram, size, stride, reject); + + if (!reject) { + errno = err; + fail_errno("exec"); + } + return err; } static char *preserved_ram(size_t size) @@ -417,10 +436,10 @@ static int shared_mm_exec(bool own_thread) if (!ram) return unsupported(); if (pipe(pipe_fd)) - return KSFT_FAIL; + return fail_errno("pipe"); if (own_thread) { if (pthread_create(&thread, NULL, blocked_thread, &pipe_fd[0])) - return KSFT_FAIL; + return fail("pthread_create failed"); check_exec(ram, size, 1, false); write(pipe_fd[1], "x", 1); pthread_join(thread, NULL); @@ -429,16 +448,18 @@ static int shared_mm_exec(bool own_thread) stack = mmap(NULL, 65536, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); if (stack == MAP_FAILED) - return KSFT_FAIL; + return fail_errno("mmap"); pid = clone(blocked_clone, stack + 65536, CLONE_VM | SIGCHLD, pipe_fd); if (pid < 0) - return KSFT_FAIL; + return fail_errno("clone"); ret = check_exec(ram, size, 1, true); /* Exec must return EBUSY with the old mapping intact. */ - if (ret != EBUSY || check_bytes(ram, size, 0x31)) - ret = KSFT_FAIL; - else - ret = KSFT_PASS; + if (ret != EBUSY) { + errno = ret; + ret = fail_errno("exec with a CLONE_VM task not refused with EBUSY"); + } else { + ret = check_bytes(ram, size, 0x31) ? fail("contents lost") : KSFT_PASS; + } write(pipe_fd[1], "x", 1); if (wait_child(pid)) ret = KSFT_FAIL; @@ -464,7 +485,7 @@ static int vhost_owner_exec(void) if (ioctl(fd, VHOST_SET_OWNER)) return KSFT_SKIP; if (ioctl(fd, VHOST_RESET_OWNER)) - return KSFT_FAIL; + return fail_errno("VHOST_RESET_OWNER"); close(fd); check_exec(ram, size, 1, false); return KSFT_FAIL; @@ -520,7 +541,7 @@ static int unprivileged_exec(const char *path, bool preserve) int err; if (pid < 0) - return KSFT_FAIL; + return fail_errno("fork"); if (pid) return wait_child(pid); if (setgroups(0, NULL) || setresgid(65534, 65534, 65534) || @@ -535,12 +556,16 @@ static int unprivileged_exec(const char *path, bool preserve) _exit(unsupported()); err = exec_path(path, ram, size, 1, true); /* Exec must return EPERM with the old mapping intact. */ - if (err != EPERM || check_bytes(ram, size, 0x31)) - _exit(KSFT_FAIL); + if (err != EPERM) { + errno = err; + _exit(fail_errno("setuid exec not refused with EPERM")); + } + if (check_bytes(ram, size, 0x31)) + _exit(fail("contents lost by a refused setuid exec")); if (madvise(ram, size, MADV_DONTEXEC)) - _exit(KSFT_FAIL); + _exit(fail_errno("MADV_DONTEXEC")); execl(path, "doexec", "--euid", NULL); - _exit(KSFT_FAIL); + _exit(fail_errno("exec of the setuid copy")); } /* The ELF opt-in must not pull memory into a more privileged image. */ @@ -575,7 +600,7 @@ static int devzero_exec(void) ram = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); close(fd); if (ram == MAP_FAILED) - return KSFT_FAIL; + return fail_errno("mmap"); memset(ram, 0x31, size); if (madvise(ram, size, MADV_DOEXEC)) return unsupported(); @@ -655,6 +680,8 @@ static int noexec_dontexec(void) } if (!madvise(ram, size, MADV_DONTEXEC) && exec_refused(ram, size)) ret = KSFT_PASS; + else + ret = fail("MADV_DONTEXEC made a noexec mapping executable"); out: umount2(dir, MNT_DETACH); rmdir(dir); @@ -670,9 +697,13 @@ static int doexec_noexec(void) if (!ram) return unsupported(); - if (!exec_refused(ram, size) || madvise(ram, size, MADV_DONTEXEC) || - mprotect(ram, size, PROT_READ | PROT_EXEC)) - return KSFT_FAIL; + if (!exec_refused(ram, size)) + return fail("marked range can be made executable"); + if (madvise(ram, size, MADV_DONTEXEC) || !smaps_has_flag(ram, " me ")) + return fail("MADV_DONTEXEC did not give VM_MAYEXEC back"); + /* SELinux execmem or MDWE may still refuse it. */ + if (mprotect(ram, size, PROT_READ | PROT_EXEC)) + return KSFT_SKIP; ret = noexec_dontexec(); if (ret == KSFT_SKIP) ksft_print_msg("noexec mount part skipped\n"); @@ -684,11 +715,11 @@ static int after_pfnmap_exec(char *bar, size_t size) size_t page = sysconf(_SC_PAGESIZE), i; if (!smaps_has_flag(bar, " pf ") || !smaps_has_flag(bar, " sh ")) - return KSFT_FAIL; + return fail("BAR not a shared PFN map after exec"); for (i = 0; i < size / page; i++) if (__atomic_load_n((uint32_t *)(bar + i * page), __ATOMIC_RELAXED) != (0xd0e00000 | i)) - return KSFT_FAIL; + return fail("BAR contents changed across exec"); return munmap(bar, size) ? KSFT_FAIL : KSFT_PASS; } @@ -709,7 +740,9 @@ static int devmem_refused(void) close(fd); if (ram == MAP_FAILED || !smaps_has_flag(ram, " pf ")) return KSFT_SKIP; - return madvise(ram, size, MADV_DOEXEC) && errno == EINVAL ? KSFT_PASS : KSFT_FAIL; + if (madvise(ram, size, MADV_DOEXEC) && errno == EINVAL) + return KSFT_PASS; + return fail("MADV_DOEXEC on a shared /dev/mem mapping not refused"); } static int vfio_device_fd(const char *bdf) @@ -764,12 +797,12 @@ static int vfio_bar_exec(void) /* Align to the size so that the BAR can be mapped with huge PFN entries. */ area = mmap(NULL, 2 * size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (area == MAP_FAILED) - return KSFT_FAIL; + return fail_errno("mmap"); aligned = ((uintptr_t)area + size - 1) & ~(size - 1); bar = mmap((void *)aligned, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, dev, info.offset); if (bar == MAP_FAILED) - return KSFT_FAIL; + return fail_errno("mmap"); for (i = 0; i < size / page; i++) __atomic_store_n((uint32_t *)(bar + i * page), 0xd0e00000 | i, __ATOMIC_RELAXED); @@ -781,7 +814,7 @@ static int vfio_bar_exec(void) (unsigned long long)(uintptr_t)bar); snprintf(size_arg, sizeof(size_arg), "%zu", size); execl("/proc/self/exe", "doexec", "--pfnmap", addr_arg, size_arg, NULL); - return KSFT_FAIL; + return fail_errno("exec"); } static void *exec_thread(void *arg) @@ -798,7 +831,7 @@ static int nonleader_exec(void) if (!ram) return unsupported(); if (pthread_create(&thread, NULL, exec_thread, ram)) - return KSFT_FAIL; + return fail("pthread_create failed"); pthread_join(thread, NULL); return KSFT_FAIL; } @@ -808,18 +841,18 @@ static int proc_exec(char *ram, size_t size, int stage) char addr_arg[32], size_arg[32], stage_arg[32]; if (check_bytes(ram, size, 0x31)) - return KSFT_FAIL; + return fail("contents lost in a repeated exec"); if (stage == 32) return KSFT_PASS; if (madvise(ram, size, MADV_DOEXEC)) - return KSFT_FAIL; + return fail_errno("MADV_DOEXEC"); snprintf(addr_arg, sizeof(addr_arg), "%llu", (unsigned long long)(uintptr_t)ram); snprintf(size_arg, sizeof(size_arg), "%zu", size); snprintf(stage_arg, sizeof(stage_arg), "%d", stage + 1); execl("/proc/self/exe", "doexec", "--proc", addr_arg, size_arg, stage_arg, NULL); - return KSFT_FAIL; + return fail_errno("exec"); } /* Read both pre-opened and freshly opened proc files during repeated exec. */ @@ -833,10 +866,10 @@ static int proc_readers_exec(void) int i; if (pipe(ready) || pipe(go)) - return KSFT_FAIL; + return fail_errno("pipe"); pid = fork(); if (pid < 0) - return KSFT_FAIL; + return fail_errno("fork"); if (!pid) { size_t size = 16 * sysconf(_SC_PAGESIZE); char *ram = preserved_ram(size); @@ -874,7 +907,8 @@ static int proc_readers_exec(void) for (;;) { waited = waitpid(pid, &status, WNOHANG); if (waited == pid) { - ret = WIFEXITED(status) ? WEXITSTATUS(status) : KSFT_FAIL; + ret = WIFEXITED(status) ? WEXITSTATUS(status) : + fail("repeated exec killed by a signal"); break; } if (waited < 0 && errno != EINTR) @@ -968,13 +1002,14 @@ static int clone_exec_race(void) pid_t pid; if (pipe(report)) - return KSFT_FAIL; + return fail_errno("pipe"); pid = fork(); if (pid < 0) - return KSFT_FAIL; + return fail_errno("fork"); if (!pid) { size_t size = sysconf(_SC_PAGESIZE); struct clone_race race = { .report = report[1] }; + const char *why = "exec refused with EBUSY every time"; pthread_t thread; unsigned int attempt; @@ -983,25 +1018,31 @@ static int clone_exec_race(void) if (!race.ram) _exit(unsupported()); if (pthread_create(&thread, NULL, clone_race_thread, &race)) - _exit(KSFT_FAIL); + _exit(fail("pthread_create failed")); while (atomic_load(&race.rounds) < 4) usleep(1000); for (attempt = 0; attempt < 500; attempt++) { - if (check_exec(race.ram, size, 1, false) != EBUSY) + errno = exec_path("/proc/self/exe", race.ram, size, 1, false); + if (errno != EBUSY) { + why = NULL; + fail_errno("exec"); break; - if (check_bytes(race.ram, size, 0x31)) + } + if (check_bytes(race.ram, size, 0x31)) { + why = "contents changed while exec was refused"; break; + } usleep(1000); } atomic_store(&race.stop, true); pthread_join(thread, NULL); - _exit(KSFT_FAIL); + _exit(why ? fail(why) : KSFT_FAIL); } close(report[1]); ret = wait_child(pid); pfd = (struct pollfd) { .fd = report[0], .events = POLLIN | POLLHUP }; if (poll(&pfd, 1, 3000) <= 0 || read(report[0], &failed, 1) != 0) - ret = KSFT_FAIL; + ret = fail("a racing CLONE_VM clone failed"); close(report[0]); return ret; } @@ -1041,7 +1082,7 @@ static int allocation_failures(void) for (nth = 1; nth <= 256; nth++) { pid = fork(); if (pid < 0) - return KSFT_FAIL; + return fail_errno("fork"); if (!pid) { char *ram, number[32]; size_t i; @@ -1052,7 +1093,7 @@ static int allocation_failures(void) MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (ram == MAP_FAILED || (madvise(ram, size, MADV_NOHUGEPAGE) && errno != EINVAL)) - _exit(KSFT_FAIL); + _exit(fail_errno("mmap or MADV_NOHUGEPAGE")); for (i = 0; i < size; i += stride) ram[i] = 0x31; if (madvise(ram, size, MADV_DOEXEC)) @@ -1062,13 +1103,13 @@ static int allocation_failures(void) fd = open("/proc/self/fail-nth", O_WRONLY | O_CLOEXEC); len = snprintf(number, sizeof(number), "%u", nth); if (exe < 0 || fd < 0 || write(fd, number, len) != len) - _exit(KSFT_FAIL); + _exit(fail_errno("fail-nth setup")); err = check_exec_fd(exe, ram, size, stride); /* Disarm before checking mappings or exiting. */ pwrite(fd, "0", 1, 0); for (i = 0; i < size; i += stride) if (ram[i] != 0x31) - _exit(KSFT_FAIL); + _exit(fail("contents lost by a failed exec")); /* * Failed allocations also surface as E2BIG from * copy_strings(), and as ELIBBAD or EIO from reading the @@ -1084,7 +1125,7 @@ static int allocation_failures(void) waited = waitpid(pid, &status, 0); } while (waited < 0 && errno == EINTR); if (waited < 0) - return KSFT_FAIL; + return fail_errno("waitpid"); if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) fatal++; else if (WIFEXITED(status) && WEXITSTATUS(status) == 10) @@ -1141,9 +1182,11 @@ static int sysctl_flag(void) static bool doexec_refused(char *addr, size_t size, const char *what) { if (addr == MAP_FAILED) { - ksft_print_msg("%s: mmap: %s\n", what, strerror(errno)); /* A security policy may forbid the mapping itself. */ - return errno == EACCES || errno == EPERM; + if (errno == EACCES || errno == EPERM) + return true; + ksft_print_msg("%s: mmap: %s\n", what, strerror(errno)); + return false; } if (madvise(addr, size, MADV_DOEXEC) && errno == EINVAL) return true; @@ -1227,7 +1270,7 @@ static int partial_exec(void) MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (ram == MAP_FAILED) - return KSFT_FAIL; + return fail_errno("mmap"); memset(ram, 0x31, 3 * page); if (madvise(ram + page, page, MADV_DOEXEC)) return unsupported(); @@ -1334,11 +1377,13 @@ static int after_fork_flag(char *ram, size_t size, const char *flag, bool wipe) return fail("contents lost"); pid = fork(); if (pid < 0) - return KSFT_FAIL; + return fail_errno("fork"); if (!pid) { - if (wipe) - _exit(range_mapped(ram, size) ? check_bytes(ram, size, 0) : KSFT_FAIL); - _exit(range_mapped(ram, size) ? KSFT_FAIL : KSFT_PASS); + if (wipe && (!range_mapped(ram, size) || check_bytes(ram, size, 0))) + _exit(fail("child of the new image did not get a wiped range")); + if (!wipe && range_mapped(ram, size)) + _exit(fail("child of the new image got a DONTFORK range")); + _exit(KSFT_PASS); } return wait_child(pid); } @@ -1581,7 +1626,7 @@ static int copy_control(void) int exe; if (pid < 0) - return KSFT_FAIL; + return fail_errno("fork"); if (!pid) { ram = preserved_ram(size); if (!ram) @@ -1604,7 +1649,7 @@ static int no_note_exec(void) err = copy_control(); if (err) - return err == KSFT_SKIP ? KSFT_SKIP : fail("unpatched copy lost the memory"); + return err == KSFT_SKIP ? KSFT_SKIP : fail("unpatched copy did not keep the memory"); ram = preserved_ram(size); if (!ram) return unsupported(); @@ -1732,7 +1777,7 @@ static int stack_overlap_exec(void) /* ...within the 128K the stack is grown by, it kills the task. */ status = stack_exec(16); if (status < 0) - return KSFT_FAIL; + return fail_errno("fork or waitpid"); if (WIFEXITED(status)) return WEXITSTATUS(status) == KSFT_SKIP ? KSFT_SKIP : fail("exec did not kill the task"); @@ -1757,7 +1802,7 @@ static int sparse_exec(void) return KSFT_SKIP; /* EINVAL: no THP support, the range is mapped with base pages anyway. */ if (madvise(ram, size, MADV_NOHUGEPAGE) && errno != EINVAL) - return KSFT_FAIL; + return fail_errno("MADV_NOHUGEPAGE"); for (i = 0; i < size; i += stride) ram[i] = 0x31; if (madvise(ram, size, MADV_DOEXEC)) @@ -1790,7 +1835,7 @@ static int swapped_exec(void) if (ram == MAP_FAILED || (madvise(ram, size, MADV_NOHUGEPAGE) && errno != EINVAL)) - return KSFT_FAIL; + return fail_errno("mmap or MADV_NOHUGEPAGE"); memset(ram, 0x31, size); if (madvise(ram, size, MADV_PAGEOUT)) return KSFT_SKIP; @@ -1824,7 +1869,7 @@ static int thp_half_exec(void) area = mmap(NULL, 2 * size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (area == MAP_FAILED) - return KSFT_FAIL; + return fail_errno("mmap"); ram = (char *)(((uintptr_t)area + size - 1) & ~(size - 1)); if (madvise(ram, size, MADV_HUGEPAGE)) return KSFT_SKIP; @@ -1873,7 +1918,7 @@ static int after_old_mm(char *ram, size_t size, int done, int check) char c; if (write(done, "x", 1) != 1 || read(check, &c, 1) != 1) - return KSFT_FAIL; + return fail("lost the parent of the old mm write case"); if (check_bytes(ram, size, 0x31)) return fail("a write through the old mm reached the new image"); return KSFT_PASS; @@ -1920,11 +1965,11 @@ static int old_mm_write_exec(void) reg.range.len = size; if (w.src == MAP_FAILED || fill == MAP_FAILED || ioctl(uffd, UFFDIO_API, &api) || ioctl(uffd, UFFDIO_REGISTER, ®) || pipe(to_child) || pipe(to_parent)) - return KSFT_FAIL; + return fail_errno("userfaultfd setup"); memset(fill, 0x77, size); pid = fork(); if (pid < 0) - return KSFT_FAIL; + return fail_errno("fork"); if (!pid) { char *ram = preserved_ram(size), args[4][32]; @@ -1942,7 +1987,7 @@ static int old_mm_write_exec(void) snprintf(args[3], sizeof(args[3]), "%d", to_child[0]); execl("/proc/self/exe", "doexec", "--old-mm", args[0], args[1], args[2], args[3], NULL); - _exit(KSFT_FAIL); + _exit(fail_errno("exec")); } close(to_child[0]); close(to_parent[1]); @@ -1957,8 +2002,10 @@ static int old_mm_write_exec(void) ret = KSFT_SKIP; goto out; } - if (pthread_create(&thread, NULL, mem_writer, &w)) + if (pthread_create(&thread, NULL, mem_writer, &w)) { + ret = fail("pthread_create failed"); goto out; + } /* The writer holds the child's mm while it faults on its source. */ pfd.fd = uffd; pfd.events = POLLIN; @@ -2009,7 +2056,7 @@ static int vhost_kthread_exec(void) if (err != EBUSY || check_bytes(ram, size, 0x31)) return fail("preserving exec with a vhost kthread not refused"); if (ioctl(fd, VHOST_RESET_OWNER)) - return KSFT_FAIL; + return fail_errno("VHOST_RESET_OWNER"); close(fd); check_exec(ram, size, 1, false); return KSFT_FAIL; @@ -2044,10 +2091,10 @@ static int vfork_exec(void) _exit(errno == EBUSY ? REFUSED : KSFT_FAIL); } if (pid < 0) - return KSFT_FAIL; + return fail_errno("fork"); if (wait_child(pid) != REFUSED) return fail("preserving exec from a vfork() child not refused"); - return check_bytes(ram, size, 0x31) ? KSFT_FAIL : KSFT_PASS; + return check_bytes(ram, size, 0x31) ? fail("contents lost by a refused exec") : KSFT_PASS; } static const struct more_case more_cases[] = { @@ -2083,7 +2130,7 @@ int main(int argc, char **argv) /* The setuid copy only reports that it runs as root. */ if (geteuid() != getuid()) return argc == 2 && !strcmp(argv[1], "--euid") && !geteuid() ? - KSFT_PASS : KSFT_FAIL; + KSFT_PASS : fail("setuid copy started in another mode"); if (argc == 7 && !strcmp(argv[1], "--exec")) return after_exec(atoi(argv[2]), (char *)(uintptr_t)strtoull(argv[3], NULL, 10), @@ -2092,7 +2139,7 @@ int main(int argc, char **argv) return after_cow_exec((char *)(uintptr_t)strtoull(argv[2], NULL, 10), strtoull(argv[3], NULL, 10), atoi(argv[4]), atoi(argv[5])); if (argc == 5 && !strcmp(argv[1], "--reject")) - return KSFT_FAIL; + return fail("exec went through instead of failing"); for (kind = 0; argc == 4 && kind < (int)ARRAY_SIZE(after_modes); kind++) if (!strcmp(argv[1], after_modes[kind].mode)) return after_modes[kind].fn((char *)(uintptr_t)strtoull(argv[2], NULL, 10), @@ -2121,8 +2168,10 @@ int main(int argc, char **argv) if (!range_mapped(ram, size)) return fail("range not preserved"); for (i = 0; i < size; i += stride) - if (ram[i] != 0x31) - return KSFT_FAIL; + if (ram[i] != 0x31) { + ksft_print_msg("byte %zu: %#x\n", i, (unsigned char)ram[i]); + return fail("preserved contents changed"); + } return KSFT_PASS; } ksft_print_header(); -- 2.43.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel