From patchwork Fri Mar 1 18:55:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Lawrence X-Patchwork-Id: 1050351 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=lists.linux.it (client-ip=2001:1418:10:5::2; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=reject dis=none) header.from=google.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b="fHbDcxoB"; dkim-atps=neutral Received: from picard.linux.it (picard.linux.it [IPv6:2001:1418:10:5::2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 449zCb4mzdz9s47 for ; Sat, 2 Mar 2019 05:56:08 +1100 (AEDT) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id DE9B3294AD9 for ; Fri, 1 Mar 2019 19:56:00 +0100 (CET) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-3.smtp.seeweb.it (in-3.smtp.seeweb.it [217.194.8.3]) by picard.linux.it (Postfix) with ESMTP id 9676E294A8D for ; Fri, 1 Mar 2019 19:55:58 +0100 (CET) Received: from mail-it1-x14a.google.com (mail-it1-x14a.google.com [IPv6:2607:f8b0:4864:20::14a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by in-3.smtp.seeweb.it (Postfix) with ESMTPS id CAA501A01550 for ; Fri, 1 Mar 2019 19:55:57 +0100 (CET) Received: by mail-it1-x14a.google.com with SMTP id q141so12393257itc.2 for ; Fri, 01 Mar 2019 10:55:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:message-id:mime-version:subject:from:to:cc; bh=qJycYbGvA7WFJC7vH3QUC43n/SWGrJJJ9iVcg20A6iY=; b=fHbDcxoBruEIHYYm9aEPZ5LQXrLRmcGl2TiVRmRoIBBRHPT5F0sVXykHamLglebILR vZvZ7e+FTsaclFropN3N1m1GjymWxBKzvwnllnKKbpO6ubmSyNnkfuBopY1zDIGUnfHq MadlO/Zs+VlKgVxKM+cQACyAeKcQrKfHuevrTHJHFPsburjvPw4flCEx62W3+AWhTVUz 3RcharxF5wraaJFBiOSr0SmcJ59nU0IQ50ATMzh2s1Yx4gPfHzNMQPEoNawX1qQ5wBf9 mmo3BssWwI7MGyKG9h8O4F5+UghXdeyhtmwAn08n09/a9aiEw7YuhVpCPDfG0G1e36FI gXMw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:message-id:mime-version:subject:from:to:cc; bh=qJycYbGvA7WFJC7vH3QUC43n/SWGrJJJ9iVcg20A6iY=; b=GmJzaaCTOdhEZcHrzgLaQaoMGMDQpetuLrGrN0zuInc/onaYiLv8HowSOzs0+bwl42 8tPMaeyI3pBg4gzb0G5VA46JPsAxe6p3RsULIIkyaAaj5gaBKVD2yhxNQ/qn50fRIi6t bHsSoDekxhjE5jmhg3YU4fE3Krh3zNEGcImAEVZoysUJ+8r6zst9xDWY7rLBvui3NyM6 oc3bN6i1GEkxPmPwY7xxXUqNLidj7WkGOXt8+v+38QgaBFoDXj006fBb5W4fhloLfXJl QUwYtkzp/yVbW9OUDpP/YUZ5C9kl83uu+RRNoOYWOE+2T5jJ5/sHYlR5TgnxmlK4v+uu N2/A== X-Gm-Message-State: APjAAAUVorkQV8d29NuL0Mz8h7fJzQdhBzABDJSeqe3Oc7tKLraoRA6m 8rx077qVquJmkPTmi/TPl9xN9YNz5o7tXWHRyTZkda2qdO0Z1YuROeXslvOpG6kB+12VRBU8Stc gLbHdnsbbD0mrBUqhOfc3bpQRlb9fTt8mAOflTT90seg/8HGqtDJKWqYbABnm5smP8Fdtm3lg X-Google-Smtp-Source: APXvYqxVe/HcUUeaR7hKYe0o0TmLOyEV/V112nPoRRZnVWfvhyOvSwyUgIoN8e41ekutL9+LjmFezgbBpTMUyxTAOug= X-Received: by 2002:a24:108b:: with SMTP id 133mr4000720ity.29.1551466556318; Fri, 01 Mar 2019 10:55:56 -0800 (PST) Date: Fri, 1 Mar 2019 10:55:34 -0800 Message-Id: <20190301185534.37513-1-paullawrence@google.com> Mime-Version: 1.0 X-Mailer: git-send-email 2.21.0.352.gf09ad66450-goog From: Paul Lawrence To: ltp@lists.linux.it, kernel-team@android.com, maennich@google.com X-Virus-Scanned: clamav-milter 0.99.2 at in-3.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=-7.4 required=7.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU, SPF_PASS, USER_IN_DEF_DKIM_WL autolearn=disabled version=3.4.0 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on in-3.smtp.seeweb.it Subject: [LTP] [PATCH v2] Add ltp pivot_root test X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.18 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "ltp" Replaced license Merged all files into one parameterized file Modified touse test_brk where appropriate For the actual test results, using tst_brk from the child process meant failures were not counted correctly, so I still use tst_res and exit. Please let me know if there is a correct way of doing this. Signed-off-by: Paul Lawrence --- testcases/kernel/syscalls/pivot_root/Makefile | 11 ++ .../kernel/syscalls/pivot_root/pivot_root01.c | 168 ++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 testcases/kernel/syscalls/pivot_root/Makefile create mode 100644 testcases/kernel/syscalls/pivot_root/pivot_root01.c diff --git a/testcases/kernel/syscalls/pivot_root/Makefile b/testcases/kernel/syscalls/pivot_root/Makefile new file mode 100644 index 000000000..ce8f8cb68 --- /dev/null +++ b/testcases/kernel/syscalls/pivot_root/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (c) 2019 Google, Inc. + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +CFLAGS += -lcap + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/syscalls/pivot_root/pivot_root01.c b/testcases/kernel/syscalls/pivot_root/pivot_root01.c new file mode 100644 index 000000000..3aa939ac9 --- /dev/null +++ b/testcases/kernel/syscalls/pivot_root/pivot_root01.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Copyright (c) 2019 Google, Inc. + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include "tst_test.h" + +static const char* chroot_dir = "chroot"; +static const char* new_root = "/new_root"; +static const char* put_old = "/new_root/put_old"; + +// Test consists of a series of steps that allow pivot_root to succeed, which is +// run when param is NORMAL. All other values tweak one of the steps to induce +// a failure, and check the errno is as expected. +#define NORMAL 0 + +// EBUSY +// new_root or put_old are on the current root file system +#define NEW_ROOT_ON_CURRENT_ROOT 1 + +// EINVAL +// put_old is not underneath new_root +// Note: if put_old and new_root are on the same fs, +// pivot_root fails with EBUSY before testing reachability +#define PUT_OLD_NOT_UNDERNEATH_NEW_ROOT 2 + +static const char* put_old_fs = "/put_old_fs"; +static const char* put_old_bad = "/put_old_fs/put_old"; + +// ENOTDIR +// new_root or put_old is not a directory +#define PUT_OLD_NOT_DIR 3 + +// EPERM +// The calling process does not have the CAP_SYS_ADMIN capability. +#define NO_CAP_SYS_ADMIN 4 + +#define TEST_COUNT 5 + +static int expected_error[] = { + 0, + EBUSY, + EINVAL, + ENOTDIR, + EPERM, +}; + +static void drop_cap_sys_admin(void) { + cap_value_t cap_value[] = { CAP_SYS_ADMIN }; + cap_t cap = cap_get_proc(); + if (!cap) + tst_brk(TFAIL | TERRNO, "cap_get_proc failed"); + + if (cap_set_flag(cap, CAP_EFFECTIVE, 1, cap_value, CAP_CLEAR)) + tst_brk(TFAIL | TERRNO, "cap_set_flag failed"); + + if (cap_set_proc(cap)) + tst_brk(TFAIL | TERRNO, "cap_set_proc failed"); +} + +#ifdef HAVE_UNSHARE +static void run(unsigned int param) +{ + // Work in child process - needed to undo unshare and chroot + if (SAFE_FORK()) { + tst_reap_children(); + return; + } + + // pivot_root requires no shared mounts exist in process namespace + TEST(unshare(CLONE_NEWNS | CLONE_FS)); + if (TST_RET == -1) + tst_brk(TFAIL | TERRNO, "unshare failed"); + + // Create an initial root dir. pivot_root doesn't work if the initial root + // dir is a initramfs, so use chroot to create a safe environment + SAFE_MOUNT("none", "/", NULL, MS_REC|MS_PRIVATE, NULL); + SAFE_MOUNT("none", chroot_dir, "tmpfs", 0, 0); + SAFE_CHROOT(chroot_dir); + + // Create our new root location + SAFE_MKDIR(new_root, 0777); + + // pivot_root only works if new_root is a mount point, so mount a tmpfs + // unless testing for that fail mode + if (param != NEW_ROOT_ON_CURRENT_ROOT) + SAFE_MOUNT("none", new_root, "tmpfs", 0, 0); + + // Create put_old under new_root, unless testing for that specific fail + // mode + const char* actual_put_old = NULL; + if (param == PUT_OLD_NOT_UNDERNEATH_NEW_ROOT) { + actual_put_old = put_old_bad; + SAFE_MKDIR(put_old_fs, 0777); + SAFE_MOUNT("none", put_old_fs, "tmpfs", 0, 0); + SAFE_MKDIR(put_old_bad, 0777); + } else { + actual_put_old = put_old; + + // put_old must be a directory for success + if (param == PUT_OLD_NOT_DIR) + SAFE_CREAT(put_old, 0777); + else + SAFE_MKDIR(put_old, 0777); + } + + if (param == NO_CAP_SYS_ADMIN) + drop_cap_sys_admin(); + + // Test the syscall + TEST(syscall(__NR_pivot_root, new_root, actual_put_old)); + + // If NORMAL it should have succeeded + if (param == NORMAL) { + if (TST_RET) { + tst_res(TFAIL | TERRNO, "pivot_root failed"); + exit(TBROK); + } else { + tst_res(TPASS, "pivot_root succeeded"); + exit(TPASS); + } + } + + // pivot_root is expected to fail + if (TST_RET == 0) { + tst_res(TFAIL, "pivot_root succeeded unexpectedly"); + exit(TBROK); + } + + // Check error code is correct + if (errno != expected_error[param]) { + tst_res(TFAIL | TERRNO, "pivot_root failed with wrong errno"); + exit(TBROK); + } + + tst_res(TPASS, "pivot_root failed as expected with %s", + strerror(errno)); + exit(TPASS); +} + +#else +static void run(void) +{ + tst_brk(TCONF, NULL, "unshare is undefined."); +} +#endif + +static void setup(void) +{ + SAFE_MKDIR(chroot_dir, 0777); +} + +static struct tst_test test = { + .test = run, + .tcnt = TEST_COUNT, + .needs_tmpdir = 1, + .needs_root = 1, + .forks_child = 1, + .setup = setup, +};