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 34FED802A5 for ; Wed, 14 Oct 2026 17:14:05 +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 69EHCpqL001374; Wed, 14 Oct 2026 20:12:51 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69EHCpqL001374 Authentication-Results: mail.openvz.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=virtuozzo.com header.i=@virtuozzo.com header.b="kKQPbM35" Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by mail.openvz.org (8.14.4/8.14.4) with ESMTP id 69EHCVcq001318 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=FAIL) for ; Wed, 14 Oct 2026 20:12:31 +0300 DKIM-Filter: OpenDKIM Filter v2.11.0 mail.openvz.org 69EHCVcq001318 Received: by mail-wr1-f69.google.com with SMTP id ffacd0b85a97d-48afe000b94so598500f8f.3 for ; Wed, 14 Oct 2026 10:12:31 -0700 (PDT) X-Gm-Message-State: AFq9FYIIDc7Z0KJaplM/HDT6iQwvKhi0WGpdExU4MPD9AOPz2Lul7pA9 ax5gRKQ6BuJBkN5pcOSU3pF1l5ntTyHSsismD8KNdqH4fvf6Ry/I2Ji1kv66lzYWkBNGrsYd6Jq FOj1qw8/DSARilE/oC30YdgpbuKO20zPUXvXVV57VDOBfSPFct1cNzw== X-Gm-Gg: AYBFou2sM/qzPo5KFl0F7ieYNr5d74zpE2Ftd80n4o9uHAS8DZ9XMOwNS3Woir2Jv03 wHBzjXy27eRDdoLwCeEn+PPV9GV3+/psniNSfWnaEuPf2nrfmS96QEepnGD9qNmjZoVaCpqwDjS +CPLjePvBEOQ17suZYxVFfltlaxK/wbv/cLKkkyqAhOVKzXeGWrACbpY71kq0fkmnEROEXATySa K0vEPpGsUTt4t3pJIa4Xvj8QVp6+Q3KWnU5slnYH0+kLIedIh6pZc/oCDAtWkKFx0tzR5bIAdzS Yx3awcvNAFOmk8XEdtP0j+dQqbK3m3Wz9kXFrhVFaWjrUkjQgYIzvMizh/DEmpRMmMtwZNX6arr 7vlOijRJsf9aQsUxfOQ== X-Received: by 2002:a05:6000:2505:b0:48b:123e:2f60 with SMTP id ffacd0b85a97d-48dbace0bb1mr32651805f8f.39.1791997950977; Wed, 14 Oct 2026 10:12:30 -0700 (PDT) X-Received: by 2002:a05:6000:2505:b0:48b:123e:2f60 with SMTP id ffacd0b85a97d-48dbace0bb1mr32651711f8f.39.1791997950318; Wed, 14 Oct 2026 10:12:30 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1791997950; cv=none; d=google.com; s=arc-20260327; b=VTTT9jywifvyeBhG3PRJlX6sNKouBYByS75PPMzAcQX07CbSiE7PThNix6U7NbrZIf 8xF/3TEgWWsUGOXD1oEQ+RQ0hJM5LGcfk1l2A6XQyM7Qnm78kkf5JCTT4kk4MHHInr0e WNzqf410PdmGlNo/bvhv/HNwEBoLzgYuIJMZF8zBZcTP+6HX9kL3x/uF6gOM5p4jaIxb YduEVw9Tivdtuo0FardW6LXFqtAjuLqDkTvh7OBUIaBtboBW/T/jMZUMo4pjN5MHQUAG TqYTMASs6UdPZb5HIa8mSnfUQfjPf1UVhbg196VGftOxSQanpM/xl/lkrItWKQQaD1Z9 B8FA== 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=7Q2RLlbWlOXHS8yyoyQeRjLGqcE/qMFeyKiMojBeXo0=; fh=xUcS46XhSaUOWFT7CPWNuABJ4EJ7/+S5u4N+e+rlWN4=; b=QCFhewbKxuHjPZVk6csumLfKDisWqdx0nuKKSFYljWWcRnxiQ3FupRJMJwX/GtDB9Y Qr1zhixNU/DKUga+g+05qiuFB9texLC1mXHrzELlHwYYgzQwncHRhTPwMuQSUaUYRK1Q jpKWU+pfhYYII3gPS2RvrF42wr6aU2p6sVxk9oi84fI/+qEBlseU9oWZC1VDFMtkDFf1 etGA/DvtzSc3olfuvkkUe+YiZgLDGJRbvvS8y4REiAwQanB4pVdtk7WVwsNPFi9ngf8K ieDyHEaYBe04bmtB9r3XVWeb90rSluYVSsxgEtCYfucwWb5tZne/dJKMwgX5aKy96aKP Cziw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@virtuozzo.com header.s=relay header.b=kKQPbM35; 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-48de0c1be9dsi12332705f8f.488.2026.10.14.10.12.30 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 14 Oct 2026 10:12:30 -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=kKQPbM35; 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=7Q2RLlbWlOXHS8yyoyQeRjLGqcE/qMFeyKiMojBeXo0=; b=kKQPbM35tqCikZTjq2z 3gIBiO4al+MhgH372puMmhnlXbdKgUI9+12NF9JUrUZdAii0IWIg3EnBE8T2Qr3/8LskRyId7feNM VVHiP6eUT/bu1eGdncq8tI/Hxdi3tumecaFTmrg99ayaNAR8irwzJgOMDJ68PeKemJEKzr+Y3uuce Zi/XO7Ak9fzC9g3kCQ8HiLDpMS8tBCmcODwVR+SygJzJM8L21JwgG1erNloAZtOvKtEYraMvqNiaC znRsXSl9n5duBpTcj5+lYA/Ok6NzI+BiZDpD6XpECHUIWK+gFo4gVlVOi4Gw78XsF+fwW0Ku8o4YB cLhvpq7FOP15Imw==; 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 1xH2TI-0082VK-2g; Wed, 14 Oct 2026 19:12:29 +0200 From: Konstantin Khorenko Date: Wed, 14 Oct 2026 19:12:25 +0200 MIME-Version: 1.0 Message-Id: <20261014-b4-prep-khorenko-vstor-149847-3-doexec-oracle-fixes-v1-6-e34cbe0d1440@virtuozzo.com> References: <20261014-b4-prep-khorenko-vstor-149847-3-doexec-oracle-fixes-v1-0-e34cbe0d1440@virtuozzo.com> In-Reply-To: <20261014-b4-prep-khorenko-vstor-149847-3-doexec-oracle-fixes-v1-0-e34cbe0d1440@virtuozzo.com> To: Pavel Tikhomirov , Andrey Drobyshev , Vasileios Almpanis X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=7365; i=khorenko@virtuozzo.com; h=from:subject:message-id; bh=9B/Rq/LMrNp9SODtHdjR9TbUPgoPn3jxlZlxc5GZyEo=; b=owEBbQKS/ZANAwAKAVGWCkf5YVwqAcsmYgBqz7f7YnKwTcP6hxvRb0gjtAGtOLcFXY+k9J5Rx p2I7CQo8gyJAjMEAAEKAB0WIQRWD23IPcXT0GO9CCxRlgpH+WFcKgUCas+3+wAKCRBRlgpH+WFc Kn01D/0WkijCq/X7b0bKW4K7kcuxVDgiY0XTD6aRnDzDhKqH6NF/EWpJLsVeONiLXApl/ZQD5i7 Tv/7xFSH1M4rYZRYSxkisRmJA2QVCx0JO4ZeycfkaRwhUOk4BVBHCACblHK05XMmqTNqNKM04Xh LPqYKIZtnUsJbDzlxP4hLp0UmQvBPP3T7fZoJ8gBw3mLAF6gaxaJbJdIQVt4YUzSv6ze2lcXW3u 1/AJo1JtR95QyovNtzUssr2x2yz4+nBLO5OHaGgXBc8ofFGBl0aYsSYClbA70AOFU1xfKn9pkP0 ArvPsyk2Y4TQgE2hd9Jz+sUp4e/Za540075rEOtaNR1DD2CQgUMSvmAud9IwwzRbiwnhM/IKmFv O5i46v62SxAIChUHVWo6pwFtSLe8FOCdsPzlcb67iWTdTBCOpIgxoNFQE99oORKJQdSiKq6RmHu wQu93z5gWaOnZT21yRXkN31LenY3yVJyBYAo54Y5gce9nLps7DepSQ2pJUPPOiaforsPxHaO8aY o/QyRpo8sICxIxjjHo0BwEO7YATlEQzIPpmYNklFtRr6aXgd02U21fjRvQ6c6qgiRvZWdrlLM80 nqv4MbbkCChxom+ZRac+zE9OEHMOIR14bNYZqU3wEICVubxlMASRwS2OgeY2+HvbP5nkJpzU3jE 06v6vCvTzHRPDbQ== 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 6/7] selftests/mm: check that a setuid exec refuses preserved memory 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 Copy the test binary, make it setuid root and exec it from an unprivileged child with a MADV_DOEXEC range: exec must fail with EPERM and leave the mapping intact, and must succeed once the range is unmarked with MADV_DONTEXEC. The case needs root and a directory that is neither private nor mounted nosuid; the current directory, /var/tmp and /tmp are tried in that order and the case is skipped if none fits. The copy goes into a new directory that others can enter but not list, and run setuid it does nothing but report its euid. https://virtuozzo.atlassian.net/browse/VSTOR-150232 Feature: oracle/mm: MADV_DOEXEC madvise() flag Assisted-by: LLM Signed-off-by: Konstantin Khorenko --- tools/testing/selftests/mm/doexec.c | 121 ++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/mm/doexec.c b/tools/testing/selftests/mm/doexec.c index 33529aa41367b..dfa3da2c43934 100644 --- a/tools/testing/selftests/mm/doexec.c +++ b/tools/testing/selftests/mm/doexec.c @@ -3,6 +3,7 @@ #define _GNU_SOURCE #include #include +#include #include #include #include @@ -17,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -27,6 +30,9 @@ #ifndef MADV_DOEXEC #define MADV_DOEXEC 201 #endif +#ifndef MADV_DONTEXEC +#define MADV_DONTEXEC 202 +#endif #ifndef MADV_COLLAPSE #define MADV_COLLAPSE 25 #endif @@ -283,7 +289,8 @@ static int before_exec(int kind) return KSFT_FAIL; } -static int check_exec(char *ram, size_t size, size_t stride, bool reject) +static int exec_path(const char *exe, char *ram, size_t size, size_t stride, + bool reject) { char addr_arg[32], size_arg[32], stride_arg[32]; @@ -291,11 +298,16 @@ static int check_exec(char *ram, size_t size, size_t stride, bool reject) (unsigned long long)(uintptr_t)ram); snprintf(size_arg, sizeof(size_arg), "%zu", size); snprintf(stride_arg, sizeof(stride_arg), "%zu", stride); - execl("/proc/self/exe", "doexec", reject ? "--reject" : "--check", addr_arg, size_arg, + execl(exe, "doexec", reject ? "--reject" : "--check", addr_arg, size_arg, stride_arg, NULL); return errno; } +static int check_exec(char *ram, size_t size, size_t stride, bool reject) +{ + return exec_path("/proc/self/exe", ram, size, stride, reject); +} + static char *preserved_ram(size_t size) { char *ram = mmap(NULL, size, PROT_READ | PROT_WRITE, @@ -397,6 +409,98 @@ static int vhost_owner_exec(void) return KSFT_FAIL; } +static void remove_setuid_copy(char *path) +{ + unlink(path); + *strrchr(path, '/') = '\0'; + rmdir(path); +} + +static int make_setuid_copy(const char *dir, char *path, size_t len) +{ + ssize_t copied = -1; + int in, out; + size_t n; + + snprintf(path, len, "%s/doexec-setuid.XXXXXX", dir); + /* Others may run the copy but cannot find it. */ + if (!mkdtemp(path)) + return -1; + if (chmod(path, 0711)) { + rmdir(path); + return -1; + } + n = strlen(path); + snprintf(path + n, len - n, "/doexec.XXXXXX"); + out = mkstemp(path); + in = open("/proc/self/exe", O_RDONLY); + if (in >= 0 && out >= 0) + while ((copied = sendfile(out, in, NULL, 1 << 20)) > 0) + ; + if (in >= 0) + close(in); + if (out >= 0 && !copied && !fchmod(out, S_ISUID | 0755)) { + close(out); + return 0; + } + if (out >= 0) + close(out); + remove_setuid_copy(path); + return -1; +} + +static int unprivileged_exec(const char *path, bool preserve) +{ + size_t size = sysconf(_SC_PAGESIZE); + pid_t pid = fork(); + char *ram; + int err; + + if (pid < 0) + return KSFT_FAIL; + if (pid) + return wait_child(pid); + if (setgroups(0, NULL) || setresgid(65534, 65534, 65534) || + setresuid(65534, 65534, 65534)) + _exit(KSFT_SKIP); + if (!preserve) { + execl(path, "doexec", "--euid", NULL); + _exit(KSFT_SKIP); + } + ram = preserved_ram(size); + if (!ram) + _exit(errno == EINVAL ? KSFT_SKIP : KSFT_FAIL); + 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 (madvise(ram, size, MADV_DONTEXEC)) + _exit(KSFT_FAIL); + execl(path, "doexec", "--euid", NULL); + _exit(KSFT_FAIL); +} + +/* The ELF opt-in must not pull memory into a more privileged image. */ +static int setuid_exec(void) +{ + static const char * const dirs[] = { ".", "/var/tmp", "/tmp" }; + char path[96]; + int i, ret = KSFT_SKIP; + + if (geteuid()) + return KSFT_SKIP; + for (i = 0; i < 3 && ret == KSFT_SKIP; i++) { + if (make_setuid_copy(dirs[i], path, sizeof(path))) + continue; + /* A nosuid mount or a private directory makes the copy useless. */ + ret = unprivileged_exec(path, false); + if (ret == KSFT_PASS) + ret = unprivileged_exec(path, true); + remove_setuid_copy(path); + } + return ret; +} + static void *exec_thread(void *arg) { check_exec(arg, sysconf(_SC_PAGESIZE), 1, false); @@ -723,6 +827,10 @@ int main(int argc, char **argv) pid_t pid; int kind, ret; + /* 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; if (argc == 7 && !strcmp(argv[1], "--exec")) return after_exec(atoi(argv[2]), (char *)(uintptr_t)strtoull(argv[3], NULL, 10), @@ -732,6 +840,8 @@ int main(int argc, char **argv) strtoull(argv[3], NULL, 10), atoi(argv[4]), atoi(argv[5])); if (argc == 5 && !strcmp(argv[1], "--reject")) return KSFT_FAIL; + if (argc == 2 && !strcmp(argv[1], "--euid")) + return geteuid() ? KSFT_SKIP : KSFT_PASS; if (argc == 5 && !strcmp(argv[1], "--proc")) return proc_exec((char *)(uintptr_t)strtoull(argv[2], NULL, 10), strtoull(argv[3], NULL, 10), atoi(argv[4])); @@ -751,7 +861,7 @@ int main(int argc, char **argv) return KSFT_PASS; } ksft_print_header(); - ksft_set_plan(NR_TESTS + 8); + ksft_set_plan(NR_TESTS + 9); for (kind = 0; kind < NR_TESTS; kind++) { fflush(stdout); pid = fork(); @@ -774,7 +884,7 @@ int main(int argc, char **argv) ksft_test_result_skip("pre-existing COW: DOEXEC unavailable\n"); else ksft_test_result(ret == KSFT_PASS, "pre-existing COW preserved\n"); - for (kind = 0; kind < 7; kind++) { + for (kind = 0; kind < 8; kind++) { static const char * const extra_names[] = { "CLONE_VM: recoverable EBUSY and retry", "own threads: preserving exec allowed", @@ -783,6 +893,7 @@ int main(int argc, char **argv) "non-leader thread: preserving exec allowed", "proc readers: repeated preserving exec allowed", "concurrent CLONE_VM: shared memory is not stolen", + "setuid exec: preserving exec refused", }; fflush(stdout); @@ -802,6 +913,8 @@ int main(int argc, char **argv) _exit(proc_readers_exec()); case 6: _exit(clone_exec_race()); + case 7: + _exit(setuid_exec()); } } ret = pid < 0 ? KSFT_FAIL : wait_child(pid); -- 2.43.0 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel