From patchwork Fri Mar 9 12:44:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Moese X-Patchwork-Id: 883602 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=213.254.12.146; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=suse.de Received: from picard.linux.it (picard.linux.it [213.254.12.146]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zyRsY3rmZz9sWr for ; Fri, 9 Mar 2018 23:44:33 +1100 (AEDT) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id 046343E757A for ; Fri, 9 Mar 2018 13:44:31 +0100 (CET) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-4.smtp.seeweb.it (in-4.smtp.seeweb.it [217.194.8.4]) by picard.linux.it (Postfix) with ESMTP id 6A6333E748C for ; Fri, 9 Mar 2018 13:44:21 +0100 (CET) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by in-4.smtp.seeweb.it (Postfix) with ESMTPS id 80AE51001529 for ; Fri, 9 Mar 2018 13:44:20 +0100 (CET) Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id F130EACCD for ; Fri, 9 Mar 2018 12:44:19 +0000 (UTC) From: Michael Moese To: ltp@lists.linux.it Date: Fri, 9 Mar 2018 13:44:16 +0100 Message-Id: <20180309124418.30271-2-mmoese@suse.de> X-Mailer: git-send-email 2.13.6 In-Reply-To: <20180309124418.30271-1-mmoese@suse.de> References: <20180309124418.30271-1-mmoese@suse.de> X-Virus-Scanned: clamav-milter 0.99.2 at in-4.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=-0.0 required=7.0 tests=SPF_PASS, T_RP_MATCHES_RCVD autolearn=disabled version=3.4.0 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on in-4.smtp.seeweb.it Subject: [LTP] [PATCH v6 1/3] Add library support for /proc/sys/kernel/tainted 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: , MIME-Version: 1.0 Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "ltp" Sometimes, it is important to detect if the kernel has issued a warning, died, or is tainted in another way. Linux provides this information in /proc/sys/kernel/tainted in the form of a bitfield. This patch provides library functions for testcases to detect, if it has tainted the kernel. The following functions will be introduced: - int tst_taint_init(unsigned int mask) check if the flags supplied as mask are supported by the running kernel, and if so, if they are not yet set. - int tst_taint_check() check if one or more of the bits specified in the mask provided to tst_taint_init() before are set. Returns 0 if those flags are not set, or the bitmask of set flags These can be used in the following way: First, during testcase setup: void setup(void) { ... tst_taint_init(TST_TAINT_W | TST_TAINT_D); } Second, check if the test triggered a bug: void run(void) { ... . test code here ... if (tst_taint_check() != 0) tst_res(TFAIL, "kernel has issues"); else tst_res(TPASS, "kernel seems to be fine"); } Signed-off-by: Michael Moese --- doc/test-writing-guidelines.txt | 42 ++++++++++++++++ include/tst_taint.h | 104 +++++++++++++++++++++++++++++++++++++++ lib/tst_taint.c | 106 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 252 insertions(+) create mode 100644 include/tst_taint.h create mode 100644 lib/tst_taint.c diff --git a/doc/test-writing-guidelines.txt b/doc/test-writing-guidelines.txt index 4c60cd66b..5082a07be 100644 --- a/doc/test-writing-guidelines.txt +++ b/doc/test-writing-guidelines.txt @@ -1320,6 +1320,48 @@ common.h:9: FAIL: check failed test.c:8: INFO: do_action(arg) failed ------------------------------------------------------------------------------- +2.2.24 Tainted kernels +^^^^^^^^^^^^^^^^^^^^^^ + +If you need to detect, if a testcase triggers a kernel warning, bug or oops, +the following can be used to detect TAINT_W or TAINT_D: + +[source,c] +------------------------------------------------------------------------------- +#include "tst_test.h" +#include "tst_taint.h" + +void setup(void) +{ + ... + tst_taint_init(TST_TAINT_W | TST_TAINT_D); + ... +} +... +void run(void) +{ + ... + if (tst_taint_check() == 0) + tst_res(TPASS, "kernel is not tainted"); + else + tst_res(TFAIL, "kernel is tainted"); +} +------------------------------------------------------------------------------- + +You have to call tst_taint_init() with non-zero flags first, preferably during +setup(). The function will generate a TCONF if the requested flags are not +fully supported on the running kernel, and TBROK if either a zero mask was +supplied or if the kernel is already tainted before executing the test. + +Then you can call tst_taint_check() during run(), which returns 0 or the +tainted flags set in /proc/sys/kernel/tainted as specified earlier. + +Depending on your kernel version, not all tainted-flags will be supported. + +For reference to tainted kernels, see kernel documentation: +Documentation/admin-guide/tainted-kernels.rst or +https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html + 2.3 Writing a testcase in shell ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/include/tst_taint.h b/include/tst_taint.h new file mode 100644 index 000000000..1039e2ddc --- /dev/null +++ b/include/tst_taint.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018 Michael Moese + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Usage example + * + * ... + * #include "tst_test.h" + * #include "tst_taint.h" + * .. + * void setup(void) + * { + * ... + * tst_taint_init(TST_TAINT_W | TST_TAINT_D)); + * ... + * } + * + * void run(void) + * { + * ... + * . test code here + * ... + * if (tst_taint_check() != 0) + * tst_res(TFAIL, "kernel has issues"); + * else + * tst_res(TPASS, "kernel seems to be fine"); + * } + * + * + * + * The above code checks, if the kernel issued a warning (TST_TAINT_W) + * or even died (TST_TAINT_D) during test execution. + * If these are set after running a test case, we most likely + * triggered a kernel bug. + */ + +#ifndef TST_TAINTED_H__ +#define TST_TAINTED_H__ + +/* + * This are all 17 flags that are present in kernel 4.15 + * see kernel/panic.c in kernel sources + * + * Not all of them are valid in all kernel versions. + */ +#define TST_TAINT_G (1 << 0) /* a module with non-GPL license loaded */ +#define TST_TAINT_F (1 << 1) /* a module was force-loaded */ +#define TST_TAINT_S (1 << 2) /* SMP with Non-SMP kernel */ +#define TST_TAINT_R (1 << 3) /* module force unloaded */ +#define TST_TAINT_M (1 << 4) /* machine check error occurred */ +#define TST_TAINT_B (1 << 5) /* page-release function found bad page */ +#define TST_TAINT_U (1 << 6) /* user requested taint flag */ +#define TST_TAINT_D (1 << 7) /* kernel died recently - OOPS or BUG */ +#define TST_TAINT_A (1 << 8) /* ACPI table has been overwritten */ +#define TST_TAINT_W (1 << 9) /* a warning has been issued by kernel */ +#define TST_TAINT_C (1 << 10) /* driver from drivers/staging was loaded */ +#define TST_TAINT_I (1 << 11) /* working around BIOS/Firmware bug */ +#define TST_TAINT_O (1 << 12) /* out of tree module loaded */ +#define TST_TAINT_E (1 << 13) /* unsigned module was loaded */ +#define TST_TAINT_L (1 << 14) /* A soft lock-up has previously occurred */ +#define TST_TAINT_K (1 << 15) /* kernel has been live-patched */ +#define TST_TAINT_X (1 << 16) /* auxiliary taint, for distro's use */ + +/* + * Initialize and prepare support for checking tainted kernel. + * + * supply the mask of TAINT-flags you want to check, for example + * (TST_TAINT_W | TST_TAINT_D) when you want to check if the kernel issued + * a warning or even reported it died. + * + * This function tests if the requested flags are supported on the + * locally running kernel. In case the tainted-flags are already set by + * the kernel, there is no reason to continue and TCONF is generated. + * + * The mask must not be zero. + */ +void tst_taint_init(unsigned int mask); + + +/* + * check if the tainted flags handed to tst_taint_init() are still not set + * during or after running the test. + * Calling this function is only allowed after tst_taint_init() was called, + * otherwise TBROK will be generated. + * + * returns 0 or a bitmask of the flags that currently tainted the kernel. + */ +unsigned int tst_taint_check(void); + + +#endif /* TST_TAINTED_H__ */ diff --git a/lib/tst_taint.c b/lib/tst_taint.c new file mode 100644 index 000000000..8d7a37b47 --- /dev/null +++ b/lib/tst_taint.c @@ -0,0 +1,106 @@ +#define TST_NO_DEFAULT_MAIN + +#include "tst_test.h" +#include "tst_taint.h" +#include "tst_safe_stdio.h" + +#define TAINT_FILE "/proc/sys/kernel/tainted" + +static unsigned int taint_mask = -1; + +static unsigned int tst_taint_read(void) +{ + unsigned int val; + + if (taint_mask == (unsigned int) -1) + tst_brk(TBROK, "need to call tst_taint_init() first"); + + SAFE_FILE_SCANF(TAINT_FILE, "%u", &val); + + return val; +} + +static int tst_taint_check_kver(unsigned int mask) +{ + int r1; + int r2; + int r3 = 0; + + if (mask & TST_TAINT_X) { + r1 = 4; + r2 = 15; + } else if (mask & TST_TAINT_K) { + r1 = 4; + r2 = 0; + } else if (mask & TST_TAINT_L) { + r1 = 3; + r2 = 17; + } else if (mask & TST_TAINT_E) { + r1 = 3; + r2 = 15; + } else if (mask & TST_TAINT_O) { + r1 = 3; + r2 = 2; + } else if (mask & TST_TAINT_I) { + r1 = 2; + r2 = 6; + r3 = 35; + } else if (mask & TST_TAINT_C) { + r1 = 2; + r2 = 6; + r3 = 28; + } else if (mask & TST_TAINT_W) { + r1 = 2; + r2 = 6; + r3 = 26; + } else if (mask & TST_TAINT_A) { + r1 = 2; + r2 = 6; + r3 = 25; + } else if (mask & TST_TAINT_D) { + r1 = 2; + r2 = 6; + r3 = 23; + } else if (mask & TST_TAINT_U) { + r1 = 2; + r2 = 6; + r3 = 21; + } else { + r1 = 2; + r2 = 6; + r3 = 16; + } + + return tst_kvercmp(r1, r2, r3); +} + +void tst_taint_init(unsigned int mask) +{ + unsigned int taint = -1; + + if (mask == 0) + tst_brk(TBROK, "mask is not allowed to be 0"); + + if (tst_taint_check_kver(mask) < 0) + tst_res(TCONF, "Kernel is too old for requested mask"); + + taint_mask = mask; + + taint = tst_taint_read(); + if ((taint & mask) != 0) + tst_brk(TBROK, "Kernel is already tainted: %u", taint); +} + + +unsigned int tst_taint_check(void) +{ + unsigned int taint = -1; + + if (taint_mask == (unsigned int) -1) + tst_brk(TBROK, "need to call tst_taint_init() first"); + + taint = tst_taint_read(); + + return (taint & taint_mask); +} +