Message ID | 20181101014928.23628-1-pengfei.xu@intel.com |
---|---|
State | Superseded |
Headers | show |
Series | [v2,1/2] Add Intel umip(User Mode Instruction Prevention) basic function tests | expand |
Any comments for new add security umip(User Mode Instruction Prevention) tests? Thanks! BR -----Original Message----- From: Xu, Pengfei Sent: Thursday, November 1, 2018 9:49 AM To: ltp@lists.linux.it; Xu, Pengfei <pengfei.xu@intel.com>; Cyril Hrubis <chrubis@suse.cz> Subject: [PATCH ltp v2 1/2] Add Intel umip(User Mode Instruction Prevention) basic function tests umip is one Intel security function, which protect(#GP exception) below instructions in user mode: * SGDT - Store Global Descriptor Table * SIDT - Store Interrupt Descriptor Table * SLDT - Store Local Descriptor Table * SMSW - Store Machine Status Word * STR - Store Task Register Add test code and scripts. Signed-off-by: Pengfei Xu <pengfei.xu@intel.com> --- testcases/kernel/security/umip/.gitignore | 1 + testcases/kernel/security/umip/Makefile | 9 ++ testcases/kernel/security/umip/umip_common.sh | 39 ++++++ testcases/kernel/security/umip/umip_func.sh | 58 +++++++++ testcases/kernel/security/umip/umip_gp_test.c | 171 ++++++++++++++++++++++++++ 5 files changed, 278 insertions(+) create mode 100644 testcases/kernel/security/umip/.gitignore create mode 100644 testcases/kernel/security/umip/Makefile create mode 100755 testcases/kernel/security/umip/umip_common.sh create mode 100755 testcases/kernel/security/umip/umip_func.sh create mode 100644 testcases/kernel/security/umip/umip_gp_test.c diff --git a/testcases/kernel/security/umip/.gitignore b/testcases/kernel/security/umip/.gitignore new file mode 100644 index 000000000..9e7022c59 --- /dev/null +++ b/testcases/kernel/security/umip/.gitignore @@ -0,0 +1 @@ +umip_gp_test diff --git a/testcases/kernel/security/umip/Makefile b/testcases/kernel/security/umip/Makefile new file mode 100644 index 000000000..972536c92 --- /dev/null +++ b/testcases/kernel/security/umip/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +INSTALL_TARGETS := *.sh + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/security/umip/umip_common.sh b/testcases/kernel/security/umip/umip_common.sh new file mode 100755 index 000000000..0841afc49 --- /dev/null +++ b/testcases/kernel/security/umip/umip_common.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2018, Intel +Corporation +# Authors: Pengfei Xu - pengfei.xu@intel.com +# Description: it's for umip test common function scripts + +. tst_test.sh + +# check kernel config already set or not +kconfig_check() +{ + config_content="$1" + result="" + + if [ -r "/boot/config-$(uname -r)" ]; then + result=$(grep -E "^$config_content" "/boot/config-$(uname -r)") + # for clear linux, kernel config saved in /lib/kernel/ + elif [ -r "/lib/kernel/config-$(uname -r)" ]; then + result=$(grep -E "^$config_content" "/lib/kernel/config-$(uname -r)") + elif [ -r "/proc/config.gz" ]; then + result=$(zcat "/proc/config.gz" | grep -E "^$config_content") + else + tst_res TINFO "No config file readable on this system" + return 1 + fi + [ -n "$result" ] || return 1 + return 0 +} + +# check /proc/cpuinfo contain test function or not +cpu_info_check() +{ + cpu_func="$1" + + [ -n "$cpu_func" ] || tst_brk TWARN "no cpu info check item" + grep -q "$cpu_func" /proc/cpuinfo || return 1 + tst_res TINFO "/proc/cpuinfo contain '$cpu_func'" + return 0 +} diff --git a/testcases/kernel/security/umip/umip_func.sh b/testcases/kernel/security/umip/umip_func.sh new file mode 100755 index 000000000..8719409e3 --- /dev/null +++ b/testcases/kernel/security/umip/umip_func.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later # Copyright (c) 2018, Intel +Corporation +# Authors: Pengfei Xu - pengfei.xu@intel.com +# Description: it's for Intel User Mode Instruction Prevention(UMIP) # +function test, below instructions should be GP exception blocked if # +Current Privilege Level (CPL) is greater than 0. +# UMIP protected instructions are as below: +# * SGDT - Store Global Descriptor Table +# * SIDT - Store Interrupt Descriptor Table +# * SLDT - Store Local Descriptor Table +# * SMSW - Store Machine Status Word +# * STR - Store Task Register +# kconfig requirement: CONFIG_X86_INTEL_UMIP=y +# cpu requirement: /proc/cpuinfo need contain umip + +TST_CNT=1 +TST_SETUP=setup +TST_TESTFUNC=do_test +TST_POS_ARGS=2 +TST_USAGE=usage +TST_NEEDS_ROOT=1 + +. tst_test.sh +. umip_common.sh + +bin_name="$1" +parm="$2" + +usage() { + cat <<__EOF + usage: ./$0 [bin_name][parm] + bin_name: Test cpu bin name like umip_gp_test and so on + parm: Test bin file parameter like 'g' and so on __EOF } + +# kernel config should set CONFIG_X86_INTEL_UMIP=y # /proc/cpuinfo +should contain umip +setup() +{ + func_name="umip" + config_umip="CONFIG_X86_INTEL_UMIP=y" + + kconfig_check "$config_umip" \ + || tst_brk TCONF "kernel config not set $config_umip" + cpu_info_check "$func_name" \ + || tst_brk TCONF "/proc/cpuinfo no umip function" +} + +# umip protect instruction should be blocked when user execute +do_test() +{ + tst_res TINFO "Test $bin_name $parm" + EXPECT_FAIL "$bin_name" "$parm" +} + +tst_run diff --git a/testcases/kernel/security/umip/umip_gp_test.c b/testcases/kernel/security/umip/umip_gp_test.c new file mode 100644 index 000000000..ae1068a60 --- /dev/null +++ b/testcases/kernel/security/umip/umip_gp_test.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * testcases/security/umip/umip_gp_test.c + * Copyright (C) 2018 Intel Corporation + * Author: Pengfei, Xu <pengfei.xu@intel.com> */ + +/* + * This test is for Intel User-Mode Execution Prevention(umip) test + * + * umip is a security feature present in new Intel Processors. + * If enabled, it prevents the execution of certain instructions + * if the Current Privilege Level (CPL) is greater than 0. + * If these instructions were executed while in CPL > 0, user space + * applications could not access to system-wide settings such as + * the global and local descriptor tables, the segment selectors to + * the current task state and the local descriptor table. + * UMIP is enabled by default at boot. + * + * History: Oct 23 2018 - created + * - Tested sgdt, sidt, sldt, smsw and str by asm in C + * all above 5 instruction should be #GP(general protection) + * exception in UMIP supproted and enabled platform, if + * disabled UMIP, will show instruction store results + * - Add parameter for each instruction test + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define GDT_LEN 10 +#define IDT_LEN 10 + +void usage(void) +{ + printf("Usage: [g][i][l][m][t][a]\n"); + printf("g Test sgdt\n"); + printf("i Test sidt\n"); + printf("l Test sldt\n"); + printf("m Test smsw\n"); + printf("t Test str\n"); + printf("a Test all\n"); +} + +static void asm_sgdt(void) +{ + int i; + unsigned char val[GDT_LEN]; + + memset(val, 0, sizeof(val)); + + printf("RESULTS SGDT save at [%p]\n", val); + printf("Initial val:0x"); + for (i = 0; i < GDT_LEN; i++) + printf("%02x", val[i]); + + asm volatile("sgdt %0\n" : "=m" (val)); + printf("\nSGDT results in val:\n"); + printf("val:0x"); + for (i = 0; i < GDT_LEN; i++) + printf("%02x", val[i]); + printf("\nDone.\n"); +} + +static void asm_sidt(void) +{ + int i; + unsigned char val[IDT_LEN]; + + memset(val, 0, sizeof(val)); + + printf("RESULTS SIDT save at [%p]\n", val); + printf("Initial val:0x"); + for (i = 0; i < IDT_LEN; i++) + printf("%02x", val[i]); + asm volatile("sidt %0\n" : "=m" (val)); + printf("\nSIDT results in val:\n"); + printf("val:0x"); + for (i = 0; i < GDT_LEN; i++) + printf("%02x", val[i]); + printf("\nDone.\n"); +} + +static void asm_sldt(void) +{ + unsigned long val; + + printf("RESULTS SLDT save at [%p]\n", &val); + printf("Initial val:0x%lx", val); + asm volatile("sldt %0\n" : "=m" (val)); + + printf("\nSLDT results in val:\n"); + printf("val:0x%lx", val); + printf("\nDone.\n"); +} + +static void asm_smsw(void) +{ + unsigned long val; + + printf("RESULTS SMSW save at [%p]\n", &val); + printf("Initial val:0x%lx", val); + asm volatile("smsw %0\n" : "=m" (val)); + + printf("\nSMSW results in val:\n"); + printf("val:0x%lx", val); + printf("\nDone.\n"); +} + +static void asm_str(void) +{ + unsigned long val; + + printf("RESULTS STR save at [%p]\n", &val); + printf("Initial val:0x%lx", val); + asm volatile("str %0\n" : "=m" (val)); + + printf("\nSTR results in val:\n"); + printf("val:0x%lx", val); + printf("\nDone.\n"); +} + +int main(int argc, char *argv[]) +{ + char parm; + + if (argc == 1) { + usage(); + exit(1); + } + else { + if (sscanf(argv[1], "%c", &parm) == 1) { + printf("1 parameters: parm=%c\n", parm); + } + else { + printf("Get parameter failed.\n"); + exit(1); + } + } + + switch (parm) { + case 'a': + printf("Test all.\n"); + asm_sgdt(); + asm_sidt(); + asm_sldt(); + asm_smsw(); + asm_str(); + break; + case 'g': + asm_sgdt(); + break; + case 'i': + asm_sidt(); + break; + case 'l': + asm_sldt(); + break; + case 'm': + asm_smsw(); + break; + case 't': + asm_str(); + break; + default: + usage(); + exit(1); + } +} -- 2.14.1 Add umip test cases into ltp/runtest/secure_umip Signed-off-by: Pengfei Xu <pengfei.xu@intel.com> --- runtest/secure_umip | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 runtest/secure_umip diff --git a/runtest/secure_umip b/runtest/secure_umip new file mode 100644 index 000000000..c93301b62 --- /dev/null +++ b/runtest/secure_umip @@ -0,0 +1,6 @@ +#DESCRIPTION: CPU security feature UMIP test +umip_protect_sgdt umip_func.sh umip_gp_test g +umip_protect_sidt umip_func.sh umip_gp_test i +umip_protect_sldt umip_func.sh umip_gp_test l +umip_protect_smsw umip_func.sh umip_gp_test m +umip_protect_str umip_func.sh umip_gp_test t -- 2.14.1
Hi! > +# check kernel config already set or not > +kconfig_check() > +{ > + config_content="$1" > + result="" > + > + if [ -r "/boot/config-$(uname -r)" ]; then > + result=$(grep -E "^$config_content" "/boot/config-$(uname -r)") > + # for clear linux, kernel config saved in /lib/kernel/ > + elif [ -r "/lib/kernel/config-$(uname -r)" ]; then > + result=$(grep -E "^$config_content" "/lib/kernel/config-$(uname -r)") > + elif [ -r "/proc/config.gz" ]; then > + result=$(zcat "/proc/config.gz" | grep -E "^$config_content") > + else > + tst_res TINFO "No config file readable on this system" > + return 1 > + fi > + [ -n "$result" ] || return 1 > + return 0 > +} Is there any other way how to find out UMIP has been enabled in kernel? I would like to avoid maitaing code that tries to look for all possible config locations if possible. > +# check /proc/cpuinfo contain test function or not > +cpu_info_check() > +{ > + cpu_func="$1" > + > + [ -n "$cpu_func" ] || tst_brk TWARN "no cpu info check item" > + grep -q "$cpu_func" /proc/cpuinfo || return 1 > + tst_res TINFO "/proc/cpuinfo contain '$cpu_func'" > + return 0 > +} > diff --git a/testcases/kernel/security/umip/umip_func.sh b/testcases/kernel/security/umip/umip_func.sh > new file mode 100755 > index 000000000..8719409e3 > --- /dev/null > +++ b/testcases/kernel/security/umip/umip_func.sh > @@ -0,0 +1,58 @@ > +#!/bin/sh > +# SPDX-License-Identifier: GPL-2.0-or-later > +# Copyright (c) 2018, Intel Corporation > +# Authors: Pengfei Xu - pengfei.xu@intel.com > +# Description: it's for Intel User Mode Instruction Prevention(UMIP) > +# function test, below instructions should be GP exception blocked if > +# Current Privilege Level (CPL) is greater than 0. > +# UMIP protected instructions are as below: > +# * SGDT - Store Global Descriptor Table > +# * SIDT - Store Interrupt Descriptor Table > +# * SLDT - Store Local Descriptor Table > +# * SMSW - Store Machine Status Word > +# * STR - Store Task Register > +# kconfig requirement: CONFIG_X86_INTEL_UMIP=y > +# cpu requirement: /proc/cpuinfo need contain umip > + > +TST_CNT=1 > +TST_SETUP=setup > +TST_TESTFUNC=do_test > +TST_POS_ARGS=2 > +TST_USAGE=usage > +TST_NEEDS_ROOT=1 > + > +. tst_test.sh > +. umip_common.sh > + > +bin_name="$1" > +parm="$2" > + > +usage() { > + cat <<__EOF > + usage: ./$0 [bin_name][parm] > + bin_name: Test cpu bin name like umip_gp_test and so on > + parm: Test bin file parameter like 'g' and so on > +__EOF > +} > + > +# kernel config should set CONFIG_X86_INTEL_UMIP=y > +# /proc/cpuinfo should contain umip > +setup() > +{ > + func_name="umip" > + config_umip="CONFIG_X86_INTEL_UMIP=y" > + > + kconfig_check "$config_umip" \ > + || tst_brk TCONF "kernel config not set $config_umip" > + cpu_info_check "$func_name" \ > + || tst_brk TCONF "/proc/cpuinfo no umip function" > +} > + > +# umip protect instruction should be blocked when user execute > +do_test() > +{ > + tst_res TINFO "Test $bin_name $parm" > + EXPECT_FAIL "$bin_name" "$parm" > +} > + > +tst_run > diff --git a/testcases/kernel/security/umip/umip_gp_test.c b/testcases/kernel/security/umip/umip_gp_test.c > new file mode 100644 > index 000000000..ae1068a60 > --- /dev/null > +++ b/testcases/kernel/security/umip/umip_gp_test.c > @@ -0,0 +1,171 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > + > +/* > + * testcases/security/umip/umip_gp_test.c > + * Copyright (C) 2018 Intel Corporation > + * Author: Pengfei, Xu <pengfei.xu@intel.com> > + */ > + > +/* > + * This test is for Intel User-Mode Execution Prevention(umip) test > + * > + * umip is a security feature present in new Intel Processors. > + * If enabled, it prevents the execution of certain instructions > + * if the Current Privilege Level (CPL) is greater than 0. It would be nice to write here in which generation of intel processors this feature was added. > + * If these instructions were executed while in CPL > 0, user space > + * applications could not access to system-wide settings such as > + * the global and local descriptor tables, the segment selectors to > + * the current task state and the local descriptor table. > + * UMIP is enabled by default at boot. > + * > + * History: Oct 23 2018 - created > + * - Tested sgdt, sidt, sldt, smsw and str by asm in C > + * all above 5 instruction should be #GP(general protection) > + * exception in UMIP supproted and enabled platform, if > + * disabled UMIP, will show instruction store results > + * - Add parameter for each instruction test Since we store the source code in git we do not track history in comments in source files as it is tracked in the git commit messages. > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > + > +#define GDT_LEN 10 > +#define IDT_LEN 10 > + > +void usage(void) > +{ > + printf("Usage: [g][i][l][m][t][a]\n"); > + printf("g Test sgdt\n"); > + printf("i Test sidt\n"); > + printf("l Test sldt\n"); > + printf("m Test smsw\n"); > + printf("t Test str\n"); > + printf("a Test all\n"); > +} > + > +static void asm_sgdt(void) > +{ > + int i; > + unsigned char val[GDT_LEN]; > + > + memset(val, 0, sizeof(val)); > + > + printf("RESULTS SGDT save at [%p]\n", val); > + printf("Initial val:0x"); > + for (i = 0; i < GDT_LEN; i++) > + printf("%02x", val[i]); > + > + asm volatile("sgdt %0\n" : "=m" (val)); > + printf("\nSGDT results in val:\n"); > + printf("val:0x"); > + for (i = 0; i < GDT_LEN; i++) > + printf("%02x", val[i]); > + printf("\nDone.\n"); > +} > + > +static void asm_sidt(void) > +{ > + int i; > + unsigned char val[IDT_LEN]; > + > + memset(val, 0, sizeof(val)); > + > + printf("RESULTS SIDT save at [%p]\n", val); > + printf("Initial val:0x"); > + for (i = 0; i < IDT_LEN; i++) > + printf("%02x", val[i]); > + asm volatile("sidt %0\n" : "=m" (val)); > + printf("\nSIDT results in val:\n"); > + printf("val:0x"); > + for (i = 0; i < GDT_LEN; i++) > + printf("%02x", val[i]); > + printf("\nDone.\n"); > +} > + > +static void asm_sldt(void) > +{ > + unsigned long val; > + > + printf("RESULTS SLDT save at [%p]\n", &val); > + printf("Initial val:0x%lx", val); > + asm volatile("sldt %0\n" : "=m" (val)); > + > + printf("\nSLDT results in val:\n"); > + printf("val:0x%lx", val); > + printf("\nDone.\n"); > +} > + > +static void asm_smsw(void) > +{ > + unsigned long val; > + > + printf("RESULTS SMSW save at [%p]\n", &val); > + printf("Initial val:0x%lx", val); > + asm volatile("smsw %0\n" : "=m" (val)); > + > + printf("\nSMSW results in val:\n"); > + printf("val:0x%lx", val); > + printf("\nDone.\n"); > +} > + > +static void asm_str(void) > +{ > + unsigned long val; > + > + printf("RESULTS STR save at [%p]\n", &val); > + printf("Initial val:0x%lx", val); > + asm volatile("str %0\n" : "=m" (val)); > + > + printf("\nSTR results in val:\n"); > + printf("val:0x%lx", val); > + printf("\nDone.\n"); > +} > + > +int main(int argc, char *argv[]) > +{ > + char parm; > + > + if (argc == 1) { > + usage(); > + exit(1); > + } > + else { > + if (sscanf(argv[1], "%c", &parm) == 1) { > + printf("1 parameters: parm=%c\n", parm); > + } > + else { > + printf("Get parameter failed.\n"); > + exit(1); > + } > + } > + > + switch (parm) { > + case 'a': > + printf("Test all.\n"); > + asm_sgdt(); > + asm_sidt(); > + asm_sldt(); > + asm_smsw(); > + asm_str(); > + break; > + case 'g': > + asm_sgdt(); > + break; > + case 'i': > + asm_sidt(); > + break; > + case 'l': > + asm_sldt(); > + break; > + case 'm': > + asm_smsw(); > + break; > + case 't': > + asm_str(); > + break; > + default: > + usage(); > + exit(1); > + } > +} The coding style is all messed up here and we also do not exit with zero at the end of the main. What happens to this process when it attepmts to execute one of these isntructions? Is it killed by a signal? I guess that we should run the instruction in a forked process here, wait it in the parent and check that it was properly killed.
On 2018-11-07 at 18:15:20 +0800, Cyril Hrubis wrote: > Hi! > > +# check kernel config already set or not > > +kconfig_check() > > +{ > > + config_content="$1" > > + result="" > > + > > + if [ -r "/boot/config-$(uname -r)" ]; then > > + result=$(grep -E "^$config_content" "/boot/config-$(uname -r)") > > + # for clear linux, kernel config saved in /lib/kernel/ > > + elif [ -r "/lib/kernel/config-$(uname -r)" ]; then > > + result=$(grep -E "^$config_content" "/lib/kernel/config-$(uname -r)") > > + elif [ -r "/proc/config.gz" ]; then > > + result=$(zcat "/proc/config.gz" | grep -E "^$config_content") > > + else > > + tst_res TINFO "No config file readable on this system" > > + return 1 > > + fi > > + [ -n "$result" ] || return 1 > > + return 0 > > +} > > Is there any other way how to find out UMIP has been enabled in kernel? > > I would like to avoid maitaing code that tries to look for all possible > config locations if possible. > Could find "Activated the Intel User Mode Instruction Prevention (UMIP) CPU feature" info in the head of dmesg, but afraid boot long time dmesg head is covered... And no sysfs file for umip to check. So could I add the info when cases failed:"possible due to kconfig not set CONFIG_X86_INTEL_UMIP=y"? > > +# check /proc/cpuinfo contain test function or not > > +cpu_info_check() > > +{ > > + cpu_func="$1" > > + > > + [ -n "$cpu_func" ] || tst_brk TWARN "no cpu info check item" > > + grep -q "$cpu_func" /proc/cpuinfo || return 1 > > + tst_res TINFO "/proc/cpuinfo contain '$cpu_func'" > > + return 0 > > +} > > diff --git a/testcases/kernel/security/umip/umip_func.sh b/testcases/kernel/security/umip/umip_func.sh > > new file mode 100755 > > index 000000000..8719409e3 > > --- /dev/null > > +++ b/testcases/kernel/security/umip/umip_func.sh > > @@ -0,0 +1,58 @@ > > +#!/bin/sh > > +# SPDX-License-Identifier: GPL-2.0-or-later > > +# Copyright (c) 2018, Intel Corporation > > +# Authors: Pengfei Xu - pengfei.xu@intel.com > > +# Description: it's for Intel User Mode Instruction Prevention(UMIP) > > +# function test, below instructions should be GP exception blocked if > > +# Current Privilege Level (CPL) is greater than 0. > > +# UMIP protected instructions are as below: > > +# * SGDT - Store Global Descriptor Table > > +# * SIDT - Store Interrupt Descriptor Table > > +# * SLDT - Store Local Descriptor Table > > +# * SMSW - Store Machine Status Word > > +# * STR - Store Task Register > > +# kconfig requirement: CONFIG_X86_INTEL_UMIP=y > > +# cpu requirement: /proc/cpuinfo need contain umip > > + > > +TST_CNT=1 > > +TST_SETUP=setup > > +TST_TESTFUNC=do_test > > +TST_POS_ARGS=2 > > +TST_USAGE=usage > > +TST_NEEDS_ROOT=1 > > + > > +. tst_test.sh > > +. umip_common.sh > > + > > +bin_name="$1" > > +parm="$2" > > + > > +usage() { > > + cat <<__EOF > > + usage: ./$0 [bin_name][parm] > > + bin_name: Test cpu bin name like umip_gp_test and so on > > + parm: Test bin file parameter like 'g' and so on > > +__EOF > > +} > > + > > +# kernel config should set CONFIG_X86_INTEL_UMIP=y > > +# /proc/cpuinfo should contain umip > > +setup() > > +{ > > + func_name="umip" > > + config_umip="CONFIG_X86_INTEL_UMIP=y" > > + > > + kconfig_check "$config_umip" \ > > + || tst_brk TCONF "kernel config not set $config_umip" > > + cpu_info_check "$func_name" \ > > + || tst_brk TCONF "/proc/cpuinfo no umip function" > > +} > > + > > +# umip protect instruction should be blocked when user execute > > +do_test() > > +{ > > + tst_res TINFO "Test $bin_name $parm" > > + EXPECT_FAIL "$bin_name" "$parm" > > +} > > + > > +tst_run > > diff --git a/testcases/kernel/security/umip/umip_gp_test.c b/testcases/kernel/security/umip/umip_gp_test.c > > new file mode 100644 > > index 000000000..ae1068a60 > > --- /dev/null > > +++ b/testcases/kernel/security/umip/umip_gp_test.c > > @@ -0,0 +1,171 @@ > > +// SPDX-License-Identifier: GPL-2.0-or-later > > + > > +/* > > + * testcases/security/umip/umip_gp_test.c > > + * Copyright (C) 2018 Intel Corporation > > + * Author: Pengfei, Xu <pengfei.xu@intel.com> > > + */ > > + > > +/* > > + * This test is for Intel User-Mode Execution Prevention(umip) test > > + * > > + * umip is a security feature present in new Intel Processors. > > + * If enabled, it prevents the execution of certain instructions > > + * if the Current Privilege Level (CPL) is greater than 0. > > It would be nice to write here in which generation of intel processors > this feature was added. > umip need Intel CPU of 11th-generation(maybe) Ice lake or newer to support. > > + * If these instructions were executed while in CPL > 0, user space > > + * applications could not access to system-wide settings such as > > + * the global and local descriptor tables, the segment selectors to > > + * the current task state and the local descriptor table. > > + * UMIP is enabled by default at boot. > > + * > > + * History: Oct 23 2018 - created > > + * - Tested sgdt, sidt, sldt, smsw and str by asm in C > > + * all above 5 instruction should be #GP(general protection) > > + * exception in UMIP supproted and enabled platform, if > > + * disabled UMIP, will show instruction store results > > + * - Add parameter for each instruction test > > Since we store the source code in git we do not track history in > comments in source files as it is tracked in the git commit messages. > Sure, will remove them. > > + */ > > + > > +#include <stdio.h> > > +#include <stdlib.h> > > +#include <string.h> > > + > > +#define GDT_LEN 10 > > +#define IDT_LEN 10 > > + > > +void usage(void) > > +{ > > + printf("Usage: [g][i][l][m][t][a]\n"); > > + printf("g Test sgdt\n"); > > + printf("i Test sidt\n"); > > + printf("l Test sldt\n"); > > + printf("m Test smsw\n"); > > + printf("t Test str\n"); > > + printf("a Test all\n"); > > +} > > + > > +static void asm_sgdt(void) > > +{ > > + int i; > > + unsigned char val[GDT_LEN]; > > + > > + memset(val, 0, sizeof(val)); > > + > > + printf("RESULTS SGDT save at [%p]\n", val); > > + printf("Initial val:0x"); > > + for (i = 0; i < GDT_LEN; i++) > > + printf("%02x", val[i]); > > + > > + asm volatile("sgdt %0\n" : "=m" (val)); > > + printf("\nSGDT results in val:\n"); > > + printf("val:0x"); > > + for (i = 0; i < GDT_LEN; i++) > > + printf("%02x", val[i]); > > + printf("\nDone.\n"); > > +} > > + > > +static void asm_sidt(void) > > +{ > > + int i; > > + unsigned char val[IDT_LEN]; > > + > > + memset(val, 0, sizeof(val)); > > + > > + printf("RESULTS SIDT save at [%p]\n", val); > > + printf("Initial val:0x"); > > + for (i = 0; i < IDT_LEN; i++) > > + printf("%02x", val[i]); > > + asm volatile("sidt %0\n" : "=m" (val)); > > + printf("\nSIDT results in val:\n"); > > + printf("val:0x"); > > + for (i = 0; i < GDT_LEN; i++) > > + printf("%02x", val[i]); > > + printf("\nDone.\n"); > > +} > > + > > +static void asm_sldt(void) > > +{ > > + unsigned long val; > > + > > + printf("RESULTS SLDT save at [%p]\n", &val); > > + printf("Initial val:0x%lx", val); > > + asm volatile("sldt %0\n" : "=m" (val)); > > + > > + printf("\nSLDT results in val:\n"); > > + printf("val:0x%lx", val); > > + printf("\nDone.\n"); > > +} > > + > > +static void asm_smsw(void) > > +{ > > + unsigned long val; > > + > > + printf("RESULTS SMSW save at [%p]\n", &val); > > + printf("Initial val:0x%lx", val); > > + asm volatile("smsw %0\n" : "=m" (val)); > > + > > + printf("\nSMSW results in val:\n"); > > + printf("val:0x%lx", val); > > + printf("\nDone.\n"); > > +} > > + > > +static void asm_str(void) > > +{ > > + unsigned long val; > > + > > + printf("RESULTS STR save at [%p]\n", &val); > > + printf("Initial val:0x%lx", val); > > + asm volatile("str %0\n" : "=m" (val)); > > + > > + printf("\nSTR results in val:\n"); > > + printf("val:0x%lx", val); > > + printf("\nDone.\n"); > > +} > > + > > +int main(int argc, char *argv[]) > > +{ > > + char parm; > > + > > + if (argc == 1) { > > + usage(); > > + exit(1); > > + } > > + else { > > + if (sscanf(argv[1], "%c", &parm) == 1) { > > + printf("1 parameters: parm=%c\n", parm); > > + } > > + else { > > + printf("Get parameter failed.\n"); > > + exit(1); > > + } > > + } > > + > > + switch (parm) { > > + case 'a': > > + printf("Test all.\n"); > > + asm_sgdt(); > > + asm_sidt(); > > + asm_sldt(); > > + asm_smsw(); > > + asm_str(); > > + break; > > + case 'g': > > + asm_sgdt(); > > + break; > > + case 'i': > > + asm_sidt(); > > + break; > > + case 'l': > > + asm_sldt(); > > + break; > > + case 'm': > > + asm_smsw(); > > + break; > > + case 't': > > + asm_str(); > > + break; > > + default: > > + usage(); > > + exit(1); > > + } > > +} > > The coding style is all messed up here and we also do not exit with zero > at the end of the main. > > What happens to this process when it attepmts to execute one of these > isntructions? Is it killed by a signal? I guess that we should run the > instruction in a forked process here, wait it in the parent and check > that it was properly killed. > Instruction will be killed by signal SIGSEGV(#11) general protection. Will follow the ltp style to write and add the signal check. Thanks! > -- > Cyril Hrubis > chrubis@suse.cz
Hi! > > Is there any other way how to find out UMIP has been enabled in kernel? > > > > I would like to avoid maitaing code that tries to look for all possible > > config locations if possible. > > > Could find "Activated the Intel User Mode Instruction Prevention (UMIP) > CPU feature" info in the head of dmesg, but afraid boot long time dmesg > head is covered... And no sysfs file for umip to check. > So could I add the info when cases failed???"possible due to > kconfig not set CONFIG_X86_INTEL_UMIP=y"? I was looking into this yesterday and even talked to our kernel devs and it seems that there is no other option than checking the .config to be sure it was enabled. So we may end up adding support for parsing the kernel config, it would be probably useful in differents parts of LTP. But to make this happen we should make it a library function and make sure it's accesible for C testcases as well. I can look into that and try to post a RFC patch later on, but it would probably take more than week to get sorted out.
Thanks Hrubis, it's OK, and it's not urgent for me. Thanks for your information! BR. On 2018-11-08 at 11:13:45 +0100, Cyril Hrubis wrote: > Hi! > > > Is there any other way how to find out UMIP has been enabled in kernel? > > > > > > I would like to avoid maitaing code that tries to look for all possible > > > config locations if possible. > > > > > Could find "Activated the Intel User Mode Instruction Prevention (UMIP) > > CPU feature" info in the head of dmesg, but afraid boot long time dmesg > > head is covered... And no sysfs file for umip to check. > > So could I add the info when cases failed???"possible due to > > kconfig not set CONFIG_X86_INTEL_UMIP=y"? > > I was looking into this yesterday and even talked to our kernel devs and > it seems that there is no other option than checking the .config to be > sure it was enabled. > > So we may end up adding support for parsing the kernel config, it would > be probably useful in differents parts of LTP. But to make this happen > we should make it a library function and make sure it's accesible for C > testcases as well. > > I can look into that and try to post a RFC patch later on, but it would > probably take more than week to get sorted out. > > -- > Cyril Hrubis > chrubis@suse.cz
diff --git a/testcases/kernel/security/umip/.gitignore b/testcases/kernel/security/umip/.gitignore new file mode 100644 index 000000000..9e7022c59 --- /dev/null +++ b/testcases/kernel/security/umip/.gitignore @@ -0,0 +1 @@ +umip_gp_test diff --git a/testcases/kernel/security/umip/Makefile b/testcases/kernel/security/umip/Makefile new file mode 100644 index 000000000..972536c92 --- /dev/null +++ b/testcases/kernel/security/umip/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +top_srcdir ?= ../../../.. + +include $(top_srcdir)/include/mk/testcases.mk + +INSTALL_TARGETS := *.sh + +include $(top_srcdir)/include/mk/generic_leaf_target.mk diff --git a/testcases/kernel/security/umip/umip_common.sh b/testcases/kernel/security/umip/umip_common.sh new file mode 100755 index 000000000..0841afc49 --- /dev/null +++ b/testcases/kernel/security/umip/umip_common.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2018, Intel Corporation +# Authors: Pengfei Xu - pengfei.xu@intel.com +# Description: it's for umip test common function scripts + +. tst_test.sh + +# check kernel config already set or not +kconfig_check() +{ + config_content="$1" + result="" + + if [ -r "/boot/config-$(uname -r)" ]; then + result=$(grep -E "^$config_content" "/boot/config-$(uname -r)") + # for clear linux, kernel config saved in /lib/kernel/ + elif [ -r "/lib/kernel/config-$(uname -r)" ]; then + result=$(grep -E "^$config_content" "/lib/kernel/config-$(uname -r)") + elif [ -r "/proc/config.gz" ]; then + result=$(zcat "/proc/config.gz" | grep -E "^$config_content") + else + tst_res TINFO "No config file readable on this system" + return 1 + fi + [ -n "$result" ] || return 1 + return 0 +} + +# check /proc/cpuinfo contain test function or not +cpu_info_check() +{ + cpu_func="$1" + + [ -n "$cpu_func" ] || tst_brk TWARN "no cpu info check item" + grep -q "$cpu_func" /proc/cpuinfo || return 1 + tst_res TINFO "/proc/cpuinfo contain '$cpu_func'" + return 0 +} diff --git a/testcases/kernel/security/umip/umip_func.sh b/testcases/kernel/security/umip/umip_func.sh new file mode 100755 index 000000000..8719409e3 --- /dev/null +++ b/testcases/kernel/security/umip/umip_func.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (c) 2018, Intel Corporation +# Authors: Pengfei Xu - pengfei.xu@intel.com +# Description: it's for Intel User Mode Instruction Prevention(UMIP) +# function test, below instructions should be GP exception blocked if +# Current Privilege Level (CPL) is greater than 0. +# UMIP protected instructions are as below: +# * SGDT - Store Global Descriptor Table +# * SIDT - Store Interrupt Descriptor Table +# * SLDT - Store Local Descriptor Table +# * SMSW - Store Machine Status Word +# * STR - Store Task Register +# kconfig requirement: CONFIG_X86_INTEL_UMIP=y +# cpu requirement: /proc/cpuinfo need contain umip + +TST_CNT=1 +TST_SETUP=setup +TST_TESTFUNC=do_test +TST_POS_ARGS=2 +TST_USAGE=usage +TST_NEEDS_ROOT=1 + +. tst_test.sh +. umip_common.sh + +bin_name="$1" +parm="$2" + +usage() { + cat <<__EOF + usage: ./$0 [bin_name][parm] + bin_name: Test cpu bin name like umip_gp_test and so on + parm: Test bin file parameter like 'g' and so on +__EOF +} + +# kernel config should set CONFIG_X86_INTEL_UMIP=y +# /proc/cpuinfo should contain umip +setup() +{ + func_name="umip" + config_umip="CONFIG_X86_INTEL_UMIP=y" + + kconfig_check "$config_umip" \ + || tst_brk TCONF "kernel config not set $config_umip" + cpu_info_check "$func_name" \ + || tst_brk TCONF "/proc/cpuinfo no umip function" +} + +# umip protect instruction should be blocked when user execute +do_test() +{ + tst_res TINFO "Test $bin_name $parm" + EXPECT_FAIL "$bin_name" "$parm" +} + +tst_run diff --git a/testcases/kernel/security/umip/umip_gp_test.c b/testcases/kernel/security/umip/umip_gp_test.c new file mode 100644 index 000000000..ae1068a60 --- /dev/null +++ b/testcases/kernel/security/umip/umip_gp_test.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * testcases/security/umip/umip_gp_test.c + * Copyright (C) 2018 Intel Corporation + * Author: Pengfei, Xu <pengfei.xu@intel.com> + */ + +/* + * This test is for Intel User-Mode Execution Prevention(umip) test + * + * umip is a security feature present in new Intel Processors. + * If enabled, it prevents the execution of certain instructions + * if the Current Privilege Level (CPL) is greater than 0. + * If these instructions were executed while in CPL > 0, user space + * applications could not access to system-wide settings such as + * the global and local descriptor tables, the segment selectors to + * the current task state and the local descriptor table. + * UMIP is enabled by default at boot. + * + * History: Oct 23 2018 - created + * - Tested sgdt, sidt, sldt, smsw and str by asm in C + * all above 5 instruction should be #GP(general protection) + * exception in UMIP supproted and enabled platform, if + * disabled UMIP, will show instruction store results + * - Add parameter for each instruction test + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define GDT_LEN 10 +#define IDT_LEN 10 + +void usage(void) +{ + printf("Usage: [g][i][l][m][t][a]\n"); + printf("g Test sgdt\n"); + printf("i Test sidt\n"); + printf("l Test sldt\n"); + printf("m Test smsw\n"); + printf("t Test str\n"); + printf("a Test all\n"); +} + +static void asm_sgdt(void) +{ + int i; + unsigned char val[GDT_LEN]; + + memset(val, 0, sizeof(val)); + + printf("RESULTS SGDT save at [%p]\n", val); + printf("Initial val:0x"); + for (i = 0; i < GDT_LEN; i++) + printf("%02x", val[i]); + + asm volatile("sgdt %0\n" : "=m" (val)); + printf("\nSGDT results in val:\n"); + printf("val:0x"); + for (i = 0; i < GDT_LEN; i++) + printf("%02x", val[i]); + printf("\nDone.\n"); +} + +static void asm_sidt(void) +{ + int i; + unsigned char val[IDT_LEN]; + + memset(val, 0, sizeof(val)); + + printf("RESULTS SIDT save at [%p]\n", val); + printf("Initial val:0x"); + for (i = 0; i < IDT_LEN; i++) + printf("%02x", val[i]); + asm volatile("sidt %0\n" : "=m" (val)); + printf("\nSIDT results in val:\n"); + printf("val:0x"); + for (i = 0; i < GDT_LEN; i++) + printf("%02x", val[i]); + printf("\nDone.\n"); +} + +static void asm_sldt(void) +{ + unsigned long val; + + printf("RESULTS SLDT save at [%p]\n", &val); + printf("Initial val:0x%lx", val); + asm volatile("sldt %0\n" : "=m" (val)); + + printf("\nSLDT results in val:\n"); + printf("val:0x%lx", val); + printf("\nDone.\n"); +} + +static void asm_smsw(void) +{ + unsigned long val; + + printf("RESULTS SMSW save at [%p]\n", &val); + printf("Initial val:0x%lx", val); + asm volatile("smsw %0\n" : "=m" (val)); + + printf("\nSMSW results in val:\n"); + printf("val:0x%lx", val); + printf("\nDone.\n"); +} + +static void asm_str(void) +{ + unsigned long val; + + printf("RESULTS STR save at [%p]\n", &val); + printf("Initial val:0x%lx", val); + asm volatile("str %0\n" : "=m" (val)); + + printf("\nSTR results in val:\n"); + printf("val:0x%lx", val); + printf("\nDone.\n"); +} + +int main(int argc, char *argv[]) +{ + char parm; + + if (argc == 1) { + usage(); + exit(1); + } + else { + if (sscanf(argv[1], "%c", &parm) == 1) { + printf("1 parameters: parm=%c\n", parm); + } + else { + printf("Get parameter failed.\n"); + exit(1); + } + } + + switch (parm) { + case 'a': + printf("Test all.\n"); + asm_sgdt(); + asm_sidt(); + asm_sldt(); + asm_smsw(); + asm_str(); + break; + case 'g': + asm_sgdt(); + break; + case 'i': + asm_sidt(); + break; + case 'l': + asm_sldt(); + break; + case 'm': + asm_smsw(); + break; + case 't': + asm_str(); + break; + default: + usage(); + exit(1); + } +}
umip is one Intel security function, which protect(#GP exception) below instructions in user mode: * SGDT - Store Global Descriptor Table * SIDT - Store Interrupt Descriptor Table * SLDT - Store Local Descriptor Table * SMSW - Store Machine Status Word * STR - Store Task Register Add test code and scripts. Signed-off-by: Pengfei Xu <pengfei.xu@intel.com> --- testcases/kernel/security/umip/.gitignore | 1 + testcases/kernel/security/umip/Makefile | 9 ++ testcases/kernel/security/umip/umip_common.sh | 39 ++++++ testcases/kernel/security/umip/umip_func.sh | 58 +++++++++ testcases/kernel/security/umip/umip_gp_test.c | 171 ++++++++++++++++++++++++++ 5 files changed, 278 insertions(+) create mode 100644 testcases/kernel/security/umip/.gitignore create mode 100644 testcases/kernel/security/umip/Makefile create mode 100755 testcases/kernel/security/umip/umip_common.sh create mode 100755 testcases/kernel/security/umip/umip_func.sh create mode 100644 testcases/kernel/security/umip/umip_gp_test.c