From nobody Mon Feb 9 01:43:00 2026 Received: from frasgout13.his.huawei.com (frasgout13.his.huawei.com [14.137.139.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EDE6618E29; Wed, 14 Feb 2024 11:31:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=14.137.139.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707910317; cv=none; b=UU8WyL98NA0QUwxug6NBO8XSQq2YjaxwNp5rYTgVyqbs5picCB6hdq+yABqIEtJHW9Y7Q6p2Acc73C6rls2wJS9wykOdafT67PEbnSbnoVxBAJBF3UvxcHE98a5Xi+XRoT73J43U3qCBjigqrwlOhJVea3IJBS3uorhfURaN834= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707910317; c=relaxed/simple; bh=IZRkWO/+sOchYJGTeX49qHMaLdDDn4RxMUwVyR1RVFI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=O88bz9EW6gRK9JOs3ubIeFktnndDqI1FenB4wbEtulCazr4NK6qn/bAgh/OPqWTG+dHxpl+1zWh1tMwgEyYW3d+HAEaGtEEQS0eqOT7AqAQa/Yc6hObr/MfaW0JUMrhT4g6S2B8FDGxZJqJttVE95vdiXVxh7YrN3U9C5DpVmnM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com; spf=pass smtp.mailfrom=huaweicloud.com; arc=none smtp.client-ip=14.137.139.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huaweicloud.com Received: from mail.maildlp.com (unknown [172.18.186.51]) by frasgout13.his.huawei.com (SkyGuard) with ESMTP id 4TZbHN35hjz9yMKw; Wed, 14 Feb 2024 19:16:32 +0800 (CST) Received: from mail02.huawei.com (unknown [7.182.16.47]) by mail.maildlp.com (Postfix) with ESMTP id AE0AD1405A2; Wed, 14 Feb 2024 19:31:36 +0800 (CST) Received: from huaweicloud.com (unknown [10.45.156.69]) by APP1 (Coremail) with SMTP id LxC2BwAn0Rl8pMxlwo99Ag--.51308S3; Wed, 14 Feb 2024 12:31:36 +0100 (CET) From: Petr Tesarik To: Jonathan Corbet , David Kaplan , Larry Dewey , Elena Reshetova , Carlos Bilbao , "Masami Hiramatsu (Google)" , Andrew Morton , Randy Dunlap , Petr Mladek , "Paul E. McKenney" , Eric DeVolder , =?UTF-8?q?Marc=20Aur=C3=A8le=20La=20France?= , "Gustavo A. R. Silva" , Nhat Pham , Greg Kroah-Hartman , "Christian Brauner (Microsoft)" , Douglas Anderson , Luis Chamberlain , Guenter Roeck , Mike Christie , Kent Overstreet , Maninder Singh , linux-doc@vger.kernel.org (open list:DOCUMENTATION), linux-kernel@vger.kernel.org (open list) Cc: Roberto Sassu , petr@tesarici.cz, Petr Tesarik Subject: [PATCH v1 1/5] sbm: SandBox Mode core data types and functions Date: Wed, 14 Feb 2024 12:30:31 +0100 Message-Id: <20240214113035.2117-2-petrtesarik@huaweicloud.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240214113035.2117-1-petrtesarik@huaweicloud.com> References: <20240214113035.2117-1-petrtesarik@huaweicloud.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: LxC2BwAn0Rl8pMxlwo99Ag--.51308S3 X-Coremail-Antispam: 1UD129KBjvJXoW3Xw4rXFyUuw15Zr48GF43Awb_yoWfArW5pF 43A3Z8Kr48ta4ay3yfJrWF9ryftw4Sgr15JFy7A343ta4qgry8WFsYqry29Fs3CrWkKayF qF1FgF10ka15Jw7anT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUm214x267AKxVWrJVCq3wAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2048vs2IY020E87I2jVAFwI0_Jr4l82xGYIkIc2 x26xkF7I0E14v26ryj6s0DM28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wA2z4x0 Y4vE2Ix0cI8IcVAFwI0_Jr0_JF4l84ACjcxK6xIIjxv20xvEc7CjxVAFwI0_Cr0_Gr1UM2 8EF7xvwVC2z280aVAFwI0_Gr0_Cr1l84ACjcxK6I8E87Iv6xkF7I0E14v26r4UJVWxJr1l e2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI 8IcVAFwI0_Jr0_Jr4lYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE7xkEbVWUJVW8JwAC jcxG0xvY0x0EwIxGrwACjI8F5VA0II8E6IAqYI8I648v4I1lFIxGxcIEc7CjxVA2Y2ka0x kIwI1lc7CjxVAKzI0EY4vE52x082I5MxAIw28IcxkI7VAKI48JMxC20s026xCaFVCjc4AY 6r1j6r4UMI8I3I0E5I8CrVAFwI0_Jr0_Jr4lx2IqxVCjr7xvwVAFwI0_JrI_JrWlx4CE17 CEb7AF67AKxVW8ZVWrXwCIc40Y0x0EwIxGrwCI42IY6xIIjxv20xvE14v26r1j6r1xMIIF 0xvE2Ix0cI8IcVCY1x0267AKxVWxJVW8Jr1lIxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMI IF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVF xhVjvjDU0xZFpf9x0JU7pnQUUUUU= X-CM-SenderInfo: hshw23xhvd2x3n6k3tpzhluzxrxghudrp/ Content-Type: text/plain; charset="utf-8" From: Petr Tesarik Introduce SandBox Mode (SBM) core infrastructure and make the feature configurable at build time with CONFIG_SANDBOX_MODE. Provide an API to execute a function in a sandbox. This target function runs in an address space that is similar to but distinct from the caller's address space. This is why the target function cannot be called directly. Instead, it is called via sbm_exec(), which takes the target function as a parameter and executes it inside the sandbox. All target functions take one void parameter and return an integer which can be interpreted as error status (zero is success, negative is error). Store sandbox parameters and state in struct sbm, and define these operations on it: * sbm_init() - set up a sandbox * sbm_destroy() - clean up sandbox resources * sbm_error() - query error status * sbm_exec() - execute code in sandbox Allow to defer error checking until after the last operation. When a SBM operation fails, set an error value in struct sbm and make it stick, that is fail all subsequent operations and return this error instead. The error value can be explicitly retrieved with sbm_error(), but simple use cases can get by with the return value of sbm_exec() alone. Also declare these arch hooks: * arch_sbm_init() - arch-specific setup * arch_sbm_destroy() - arch-specific cleanup * arch_sbm_exec() - arch-specific code execution These hooks are required to provide strong isolation. The availability of arch hooks is indicated by CONFIG_HAVE_ARCH_SBM. Initially, no architecture provides SBM arch hooks, falling back to a trivial no-op implementation. Signed-off-by: Petr Tesarik --- include/linux/sbm.h | 154 ++++++++++++++++++++++++++++++++++++++++++++ init/Kconfig | 2 + kernel/Kconfig.sbm | 31 +++++++++ kernel/Makefile | 1 + kernel/sbm.c | 45 +++++++++++++ 5 files changed, 233 insertions(+) create mode 100644 include/linux/sbm.h create mode 100644 kernel/Kconfig.sbm create mode 100644 kernel/sbm.c diff --git a/include/linux/sbm.h b/include/linux/sbm.h new file mode 100644 index 000000000000..8e0c63fb9fb2 --- /dev/null +++ b/include/linux/sbm.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH + * + * Author: Petr Tesarik + * + * SandBox Mode (SBM) public API declarations. + */ +#ifndef __LINUX_SBM_H +#define __LINUX_SBM_H + +/** + * struct sbm - SandBox Mode instance. + * @error: Error code. Initialized to zero by sbm_init() and updated wh= en + * a SBM operation fails. + * @private: Arch-specific private data. + */ +struct sbm { +#ifdef CONFIG_SANDBOX_MODE + int error; + void *private; +#endif +}; + +/** + * typedef sbm_func - Sandbox mode function pointer. + * @data: Arbitrary data passed via sbm_exec(). + * + * Return: Zero on success, negative on error. + */ +typedef int (*sbm_func)(void *data); + +#ifdef CONFIG_SANDBOX_MODE + +/** + * sbm_init() - Initialize a SandBox Mode instance. + * @sbm: SBM instance. + * + * Initialize a SBM instance structure. + * + * Return: Zero on success, negative on error. + */ +int sbm_init(struct sbm *sbm); + +/** + * sbm_destroy() - Clean up a SandBox Mode instance. + * @sbm: SBM instance to be cleaned up. + */ +void sbm_destroy(struct sbm *sbm); + +/** + * sbm_error() - Get SBM error status. + * @sbm: SBM instance. + * + * Get the SBM error code. This can be used to distinguish between + * errors returned by the target function and errors from setting + * up the sandbox environment. + */ +static inline int sbm_error(const struct sbm *sbm) +{ + return sbm->error; +} + +/** + * sbm_exec() - Execute function in a sandbox. + * @sbm: SBM instance. + * @func: Function to be called. + * @data: Argument for @func. + * + * Execute @func in a fully prepared SBM instance. + * + * Return: Return value of @func on success, or a negative error code. + */ +int sbm_exec(struct sbm *sbm, sbm_func func, void *data); + +#ifdef CONFIG_HAVE_ARCH_SBM + +/** + * arch_sbm_init() - Arch hook to initialize a SBM instance. + * @sbm: Instance to be initialized. + * + * Perform any arch-specific initialization. This hook is called by sbm_in= it() + * immediately after zeroing out @sbm. + * + * Return: Zero on success, negative error code on failure. + */ +int arch_sbm_init(struct sbm *sbm); + +/** + * arch_sbm_destroy() - Arch hook to clean up a SBM instance. + * @sbm: Instance to be cleaned up. + * + * Perform any arch-specific cleanup. This hook is called by sbm_destroy()= as + * the very last operation on @sbm. + */ +void arch_sbm_destroy(struct sbm *sbm); + +/** + * arch_sbm_exec() - Arch hook to execute code in a sandbox. + * @sbm: SBM instance. + * @func: Function to be executed in a sandbox. + * @data: Argument passed to @func. + * + * Execute @func in a fully prepared SBM instance. If sandbox mode + * cannot be set up or is aborted, set &sbm->error to a negative error + * value. This error is then returned by sbm_exec(), overriding the + * return value of arch_sbm_exec(). + * + * Return: Return value of @func. + */ +int arch_sbm_exec(struct sbm *sbm, sbm_func func, void *data); + +#else /* !CONFIG_HAVE_ARCH_SBM */ + +static inline int arch_sbm_init(struct sbm *sbm) +{ + return 0; +} + +static inline void arch_sbm_destroy(struct sbm *sbm) +{ +} + +static inline int arch_sbm_exec(struct sbm *sbm, sbm_func func, void *data) +{ + return func(data); +} + +#endif /* CONFIG_HAVE_ARCH_SBM */ + +#else /* !CONFIG_SANDBOX_MODE */ + +static inline int sbm_init(struct sbm *sbm) +{ + return 0; +} + +static inline void sbm_destroy(struct sbm *sbm) +{ +} + +static inline int sbm_error(const struct sbm *sbm) +{ + return 0; +} + +static inline int sbm_exec(struct sbm *sbm, sbm_func func, void *data) +{ + return func(data); +} + +#endif /* CONFIG_SANDBOX_MODE */ + +#endif /* __LINUX_SBM_H */ diff --git a/init/Kconfig b/init/Kconfig index 8d4e836e1b6b..253ac8c45527 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1932,6 +1932,8 @@ config TRACEPOINTS =20 source "kernel/Kconfig.kexec" =20 +source "kernel/Kconfig.sbm" + endmenu # General setup =20 source "arch/Kconfig" diff --git a/kernel/Kconfig.sbm b/kernel/Kconfig.sbm new file mode 100644 index 000000000000..64d683cefd4d --- /dev/null +++ b/kernel/Kconfig.sbm @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH +# +# Author: Petr Tesarik +# +# SandBox Mode (SBM) config options. +# + +config HAVE_ARCH_SBM + def_bool n + +config SANDBOX_MODE + bool "SandBox Mode (SBM)" + default n + help + SandBox Mode provides kernel API to run native kernel functions in a + sandbox, preventing out-of-bounds memory accesses. On targets which + implement SBM arch hooks, the isolation is strong, preventing all + memory accesses outside the sandbox; after a protection violation, + the affected kernel thread can continue running. On all other + targets, the isolation is weak, preventing only buffer overflows + within a guard page; after a violation, the kernel thread usually + terminates. + + This is an opt-in self-defense mechanism, i.e. kernel source code + must be modified to run in SandBox Mode. For such code, there is + some run-time overhead (CPU time, memory) associated with entering + and leaving the sandbox. + + If unsure, say N. diff --git a/kernel/Makefile b/kernel/Makefile index ce105a5558fc..ecc4bfd6213f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -115,6 +115,7 @@ obj-$(CONFIG_HAVE_STATIC_CALL) +=3D static_call.o obj-$(CONFIG_HAVE_STATIC_CALL_INLINE) +=3D static_call_inline.o obj-$(CONFIG_CFI_CLANG) +=3D cfi.o obj-$(CONFIG_NUMA) +=3D numa.o +obj-$(CONFIG_SANDBOX_MODE) +=3D sbm.o =20 obj-$(CONFIG_PERF_EVENTS) +=3D events/ =20 diff --git a/kernel/sbm.c b/kernel/sbm.c new file mode 100644 index 000000000000..9a5b89a71a23 --- /dev/null +++ b/kernel/sbm.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH + * + * Author: Petr Tesarik + * + * SandBox Mode (SBM) public API and generic functions. + */ + +#include +#include +#include + +int sbm_init(struct sbm *sbm) +{ + memset(sbm, 0, sizeof(*sbm)); + + sbm->error =3D arch_sbm_init(sbm); + if (sbm->error) + return sbm->error; + + return 0; +} +EXPORT_SYMBOL(sbm_init); + +void sbm_destroy(struct sbm *sbm) +{ + arch_sbm_destroy(sbm); +} +EXPORT_SYMBOL(sbm_destroy); + +int sbm_exec(struct sbm *sbm, sbm_func func, void *args) +{ + int ret; + + if (sbm->error) + return sbm->error; + + ret =3D arch_sbm_exec(sbm, func, args); + if (sbm->error) + return sbm->error; + + return ret; +} +EXPORT_SYMBOL(sbm_exec); --=20 2.34.1 From nobody Mon Feb 9 01:43:00 2026 Received: from frasgout12.his.huawei.com (frasgout12.his.huawei.com [14.137.139.154]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8742C19473; Wed, 14 Feb 2024 11:31:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=14.137.139.154 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707910319; cv=none; b=OhkCKPLHnZ4dzsWFPb8VzAb8bKamL+QCQxRKnYGSsjp6mceCLCdc5ficCDXhpPRrmJeL2sxeCKSLpHNIYcW0jkgCE9sHPnz75GPVltJoPeRB5kVAmC2TH5SMIrmvTOfZkXkqd/u62Z25liMPWAWwlUKXilNkYjf3Df1JScOi7jA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707910319; c=relaxed/simple; bh=6+0IzA4B2DlV4NvMNithN+HguWo+5w/SReDep9hW9Lo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Pv6weYLPvZC/aMptg+6M0LFjFDcpKT3FYuIkWp8qcxVoEkrItSnq+RSqYUMAa30+Gknj4cEnKGW5/sRsDG/uO7ah6Dk64egjO5DPNfW+ZTBgLhFDSq/XS4FJTT8R5wRGDO6x8pBuHnTpoduWwQTt36RJ2YGz6BZoXU8lxfQMXZU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com; spf=pass smtp.mailfrom=huaweicloud.com; arc=none smtp.client-ip=14.137.139.154 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huaweicloud.com Received: from mail.maildlp.com (unknown [172.18.186.29]) by frasgout12.his.huawei.com (SkyGuard) with ESMTP id 4TZbC34nB3z9y62P; Wed, 14 Feb 2024 19:12:47 +0800 (CST) Received: from mail02.huawei.com (unknown [7.182.16.47]) by mail.maildlp.com (Postfix) with ESMTP id 887C014061E; Wed, 14 Feb 2024 19:31:49 +0800 (CST) Received: from huaweicloud.com (unknown [10.45.156.69]) by APP1 (Coremail) with SMTP id LxC2BwAn0Rl8pMxlwo99Ag--.51308S4; Wed, 14 Feb 2024 12:31:48 +0100 (CET) From: Petr Tesarik To: Jonathan Corbet , David Kaplan , Larry Dewey , Elena Reshetova , Carlos Bilbao , "Masami Hiramatsu (Google)" , Andrew Morton , Randy Dunlap , Petr Mladek , "Paul E. McKenney" , Eric DeVolder , =?UTF-8?q?Marc=20Aur=C3=A8le=20La=20France?= , "Gustavo A. R. Silva" , Nhat Pham , Greg Kroah-Hartman , "Christian Brauner (Microsoft)" , Douglas Anderson , Luis Chamberlain , Guenter Roeck , Mike Christie , Kent Overstreet , Maninder Singh , linux-doc@vger.kernel.org (open list:DOCUMENTATION), linux-kernel@vger.kernel.org (open list) Cc: Roberto Sassu , petr@tesarici.cz, Petr Tesarik Subject: [PATCH v1 2/5] sbm: sandbox input and output buffers Date: Wed, 14 Feb 2024 12:30:32 +0100 Message-Id: <20240214113035.2117-3-petrtesarik@huaweicloud.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240214113035.2117-1-petrtesarik@huaweicloud.com> References: <20240214113035.2117-1-petrtesarik@huaweicloud.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: LxC2BwAn0Rl8pMxlwo99Ag--.51308S4 X-Coremail-Antispam: 1UD129KBjvJXoW3Xw48XrWUGw1rZFy8GF4rXwb_yoWDJF4DpF n8tFn8GF45Jry7Jrsxtr4F9w4ftw4IqF1UKay7W34YyFy5trn7WFykJr9FqFsrCrZrGayr Jr1vgay8Ga45J3DanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUm214x267AKxVWrJVCq3wAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2048vs2IY020E87I2jVAFwI0_Jryl82xGYIkIc2 x26xkF7I0E14v26ryj6s0DM28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wA2z4x0 Y4vE2Ix0cI8IcVAFwI0_Jr0_JF4l84ACjcxK6xIIjxv20xvEc7CjxVAFwI0_Cr0_Gr1UM2 8EF7xvwVC2z280aVAFwI0_Gr0_Cr1l84ACjcxK6I8E87Iv6xkF7I0E14v26r4UJVWxJr1l e2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I8CrVC2j2WlYx0E2Ix0cI 8IcVAFwI0_Jr0_Jr4lYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE7xkEbVWUJVW8JwAC jcxG0xvY0x0EwIxGrwACjI8F5VA0II8E6IAqYI8I648v4I1lFIxGxcIEc7CjxVA2Y2ka0x kIwI1lc7CjxVAKzI0EY4vE52x082I5MxAIw28IcxkI7VAKI48JMxC20s026xCaFVCjc4AY 6r1j6r4UMI8I3I0E5I8CrVAFwI0_Jr0_Jr4lx2IqxVCjr7xvwVAFwI0_JrI_JrWlx4CE17 CEb7AF67AKxVW8ZVWrXwCIc40Y0x0EwIxGrwCI42IY6xIIjxv20xvE14v26r1j6r1xMIIF 0xvE2Ix0cI8IcVCY1x0267AKxVWxJVW8Jr1lIxAIcVCF04k26cxKx2IYs7xG6r1j6r1xMI IF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVF xhVjvjDU0xZFpf9x0JUzWlgUUUUU= X-CM-SenderInfo: hshw23xhvd2x3n6k3tpzhluzxrxghudrp/ Content-Type: text/plain; charset="utf-8" From: Petr Tesarik Provide SBM_COPY_IN(), SBM_COPY_OUT() and SBM_COPY_INOUT() macros to allocate sandbox mode buffers for input/output data. Input data is copied from kernel mode to the allocated buffer before calling the target function. Output data is copied from the allocated buffer to kernel mode after the target function returns. Define two new arch hooks to map input/output buffers: * arch_sbm_map_readonly() * arch_sbm_map_writable() Before calling the target function, use these hooks to create mappings for all buffers, read-only or writable as appropriate. Provide a fallback no-op implementation. Upon expansion, the SBM_COPY_xxx() macros evaluate to the address of the buffer in sandbox mode, cast back to the original type. This pointer should be used by code running in the sandbox. It should not be used in kernel mode; although the address is valid, the buffers are overwritten by sbm_exec(). To do the typecast, prefer typeof(({x;})) over typeof(x). The statement expression forces array-to-pointer decay, which allows to pass an array as an argument to these macros. Signed-off-by: Petr Tesarik --- include/linux/sbm.h | 154 ++++++++++++++++++++++++++++++++++++++++++++ kernel/sbm.c | 88 +++++++++++++++++++++++++ 2 files changed, 242 insertions(+) diff --git a/include/linux/sbm.h b/include/linux/sbm.h index 8e0c63fb9fb2..9671b3c556c7 100644 --- a/include/linux/sbm.h +++ b/include/linux/sbm.h @@ -9,16 +9,27 @@ #ifndef __LINUX_SBM_H #define __LINUX_SBM_H =20 +struct sbm_buf; + /** * struct sbm - SandBox Mode instance. * @error: Error code. Initialized to zero by sbm_init() and updated wh= en * a SBM operation fails. * @private: Arch-specific private data. + * @input: Input data. Copied to a temporary buffer before starting san= dbox + * mode. + * @output: Output data. Copied from a temporary buffer after return from + * sandbox mode. + * @io: Input and output data. Copied to a temporary buffer before + * starting sandbox mode and copied back after return. */ struct sbm { #ifdef CONFIG_SANDBOX_MODE int error; void *private; + struct sbm_buf *input; + struct sbm_buf *output; + struct sbm_buf *io; #endif }; =20 @@ -73,6 +84,103 @@ static inline int sbm_error(const struct sbm *sbm) */ int sbm_exec(struct sbm *sbm, sbm_func func, void *data); =20 +/** + * struct sbm_buf - Description of an input/output buffer. + * @next: Pointer to the next buffer in the list. + * @kern_ptr: Buffer address in kernel mode. + * @sbm_ptr: Buffer address in sandbox mode. + * @size: Size of the buffer. + */ +struct sbm_buf { + struct sbm_buf *next; + void *kern_ptr; + void *sbm_ptr; + size_t size; +}; + +/** + * sbm_alloc_buf() - Allocate a new input/output buffer. + * @sbm: SBM instance. + * @size: Size of the buffer. + * + * Allocate a new &struct sbm_buf and the corresponding sandbox mode + * input/output buffer. If either allocation fails, update &sbm->error. + * + * Return: New buffer descriptor, or %NULL on allocation failure. + */ +struct sbm_buf *sbm_alloc_buf(struct sbm *sbm, size_t size); + +/** + * sbm_add_buf() - Add a new I/O buffer to the SBM instance. + * @sbm: SBM instance. + * @list: Target argument buffer list. + * @buf: Buffer virtual address. + * @size: Size of the buffer. + * + * Add a new buffer to @list. + * + * Return: SBM address of the buffer, or %NULL on error. + */ +static inline void *sbm_add_buf(struct sbm *sbm, struct sbm_buf **list, + const void *buf, size_t size) +{ + struct sbm_buf *io; + + io =3D sbm_alloc_buf(sbm, size); + if (!io) + return NULL; + + io->kern_ptr =3D (void *)buf; + io->next =3D *list; + *list =3D io; + return io->sbm_ptr; +} + +/** + * SBM_COPY_IN() - Mark an input buffer for copying into SBM. + * @sbm: SBM instance. + * @buf: Buffer virtual address. + * @size: Size of the buffer. + * + * Add a buffer to the input buffer list for @sbm. The content of the + * buffer is copied to sandbox mode before calling the target function. + * + * It is OK to modify the input buffer after invoking this macro. + * + * Return: Buffer address in sandbox mode. + */ +#define SBM_COPY_IN(sbm, buf, size) \ + ((typeof(({buf; })))sbm_add_buf((sbm), &(sbm)->input, (buf), (size))) + +/** + * SBM_COPY_OUT() - Mark an output buffer for copying out of SBM. + * @sbm: SBM instance. + * @buf: Buffer virtual address. + * @size: Size of the buffer. + * + * Add a buffer to the output buffer list for @sbm. The content of the + * buffer is copied to kernel mode after calling the target function. + * + * Return: Buffer address in sandbox mode. + */ +#define SBM_COPY_OUT(sbm, buf, size) \ + ((typeof(({buf; })))sbm_add_buf((sbm), &(sbm)->output, (buf), (size))) + +/** + * SBM_COPY_INOUT() - Mark an input buffer for copying into SBM and out of= SBM. + * @sbm: SBM instance. + * @buf: Buffer virtual address. + * @size: Size of the buffer. + * + * Add a buffer to the input and output buffer list for @sbm. The content + * of the buffer is copied to sandbox mode before calling the target funct= ion + * and copied back to kernel mode after the call. + * + * Return: Buffer address in sandbox mode. + */ +#define SBM_COPY_INOUT(sbm, buf, size) \ + ((typeof(({buf; })))sbm_add_buf((sbm), &(sbm)->io, (buf), (size))) + #ifdef CONFIG_HAVE_ARCH_SBM =20 /** @@ -95,6 +203,30 @@ int arch_sbm_init(struct sbm *sbm); */ void arch_sbm_destroy(struct sbm *sbm); =20 +/** + * arch_sbm_map_readonly() - Arch hook to map a buffer for reading. + * @sbm: SBM instance. + * @buf: Buffer to be mapped. + * + * Make the specified buffer readable by sandbox code. See also + * arch_sbm_map_writable(). + * + * Return: Zero on success, negative on error. + */ +int arch_sbm_map_readonly(struct sbm *sbm, const struct sbm_buf *buf); + +/** + * arch_sbm_map_writable() - Arch hook to map a buffer for reading and wri= ting. + * @sbm: SBM instance. + * @buf: Buffer to be mapped. + * + * Make the specified buffer readable and writable by sandbox code. + * See also arch_sbm_map_readonly(). + * + * Return: Zero on success, negative on error. + */ +int arch_sbm_map_writable(struct sbm *sbm, const struct sbm_buf *buf); + /** * arch_sbm_exec() - Arch hook to execute code in a sandbox. * @sbm: SBM instance. @@ -121,6 +253,18 @@ static inline void arch_sbm_destroy(struct sbm *sbm) { } =20 +static inline int arch_sbm_map_readonly(struct sbm *sbm, + const struct sbm_buf *buf) +{ + return 0; +} + +static inline int arch_sbm_map_writable(struct sbm *sbm, + const struct sbm_buf *buf) +{ + return 0; +} + static inline int arch_sbm_exec(struct sbm *sbm, sbm_func func, void *data) { return func(data); @@ -149,6 +293,16 @@ static inline int sbm_exec(struct sbm *sbm, sbm_func f= unc, void *data) return func(data); } =20 +/* Evaluate expression exactly once, avoiding warnings about a "statement + * with no effect". GCC doesn't issue this warning for the return value + * of a statement expression. + */ +#define __SBM_EVAL(x) ({ typeof(({x; })) __tmp =3D (x); __tmp; }) + +#define SBM_COPY_IN(sbm, buf, size) __SBM_EVAL(buf) +#define SBM_COPY_OUT(sbm, buf, size) __SBM_EVAL(buf) +#define SBM_COPY_INOUT(sbm, buf, size) __SBM_EVAL(buf) + #endif /* CONFIG_SANDBOX_MODE */ =20 #endif /* __LINUX_SBM_H */ diff --git a/kernel/sbm.c b/kernel/sbm.c index 9a5b89a71a23..df57184f5d87 100644 --- a/kernel/sbm.c +++ b/kernel/sbm.c @@ -9,7 +9,46 @@ =20 #include #include +#include #include +#include + +struct sbm_buf *sbm_alloc_buf(struct sbm *sbm, size_t size) +{ + struct sbm_buf *buf; + + if (sbm->error) + return NULL; + + buf =3D kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) { + sbm->error =3D -ENOMEM; + goto out; + } + buf->sbm_ptr =3D vzalloc(size); + if (!buf->sbm_ptr) { + sbm->error =3D -ENOMEM; + goto out; + } + buf->size =3D size; + +out: + return buf; +} +EXPORT_SYMBOL(sbm_alloc_buf); + +/* Free a buffer list. */ +static void sbm_free_buf_list(const struct sbm_buf *buf) +{ + const struct sbm_buf *nextbuf; + + while (buf) { + vfree(buf->sbm_ptr); + nextbuf =3D buf->next; + kfree(buf); + buf =3D nextbuf; + } +} =20 int sbm_init(struct sbm *sbm) { @@ -25,14 +64,61 @@ EXPORT_SYMBOL(sbm_init); =20 void sbm_destroy(struct sbm *sbm) { + sbm_free_buf_list(sbm->input); + sbm_free_buf_list(sbm->output); + sbm_free_buf_list(sbm->io); arch_sbm_destroy(sbm); } EXPORT_SYMBOL(sbm_destroy); =20 +/* Copy input buffers into a sandbox. */ +static int sbm_copy_in(struct sbm *sbm) +{ + const struct sbm_buf *buf; + int err =3D 0; + + for (buf =3D sbm->input; buf; buf =3D buf->next) { + err =3D arch_sbm_map_readonly(sbm, buf); + if (err) + return err; + memcpy(buf->sbm_ptr, buf->kern_ptr, buf->size); + } + + for (buf =3D sbm->io; buf; buf =3D buf->next) { + err =3D arch_sbm_map_writable(sbm, buf); + if (err) + return err; + memcpy(buf->sbm_ptr, buf->kern_ptr, buf->size); + } + + for (buf =3D sbm->output; buf; buf =3D buf->next) { + err =3D arch_sbm_map_writable(sbm, buf); + if (err) + return err; + } + + return 0; +} + +/* Copy output buffers out of a sandbox. */ +static void sbm_copy_out(struct sbm *sbm) +{ + const struct sbm_buf *buf; + + for (buf =3D sbm->output; buf; buf =3D buf->next) + memcpy(buf->kern_ptr, buf->sbm_ptr, buf->size); + for (buf =3D sbm->io; buf; buf =3D buf->next) + memcpy(buf->kern_ptr, buf->sbm_ptr, buf->size); +} + int sbm_exec(struct sbm *sbm, sbm_func func, void *args) { int ret; =20 + if (sbm->error) + return sbm->error; + + sbm->error =3D sbm_copy_in(sbm); if (sbm->error) return sbm->error; =20 @@ -40,6 +126,8 @@ int sbm_exec(struct sbm *sbm, sbm_func func, void *args) if (sbm->error) return sbm->error; =20 + sbm_copy_out(sbm); + return ret; } EXPORT_SYMBOL(sbm_exec); --=20 2.34.1 From nobody Mon Feb 9 01:43:00 2026 Received: from frasgout11.his.huawei.com (frasgout11.his.huawei.com [14.137.139.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9155A1BC2C; Wed, 14 Feb 2024 11:32:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=14.137.139.23 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707910340; cv=none; b=Vk13WCSWaqzr21h1dtf8DjZ/yeQ56T4PgllhheYdqANic+2uFcO18oQn+uit4EmZ4nhaHxYMSkCqmCW9t532CYKUWJzHY7b0wTXoXGF4QcgIwsh5x9OFjMiDUQRmmduGVu0jhgAnbR9pEZ5VO686LPjIkPKmqXWminKzoojBcYA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707910340; c=relaxed/simple; bh=toWcMARzYilY+SoIGqBpKXvEhdozlZlMae/6ffMliPE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=bp20iIOHv4DJfRVDBeMzqQa0v40+AhbH1dj/5sKwRPdOnqceNbw1GsuduNGe5rNpcx9rEtQBmQJmSdM9nKA0TAsFhS/LileB966pBgb/oKnFMwYB/nJo37ZJzuHxyEOTVSGLerrnroFstvq4NFqg5PnANYhjmUKbJ96fAYlGrGU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com; spf=pass smtp.mailfrom=huaweicloud.com; arc=none smtp.client-ip=14.137.139.23 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huaweicloud.com Received: from mail.maildlp.com (unknown [172.18.186.51]) by frasgout11.his.huawei.com (SkyGuard) with ESMTP id 4TZbHn2Ylyz9xrsF; Wed, 14 Feb 2024 19:16:53 +0800 (CST) Received: from mail02.huawei.com (unknown [7.182.16.47]) by mail.maildlp.com (Postfix) with ESMTP id 42C641405A2; Wed, 14 Feb 2024 19:32:02 +0800 (CST) Received: from huaweicloud.com (unknown [10.45.156.69]) by APP1 (Coremail) with SMTP id LxC2BwAn0Rl8pMxlwo99Ag--.51308S5; Wed, 14 Feb 2024 12:32:01 +0100 (CET) From: Petr Tesarik To: Jonathan Corbet , David Kaplan , Larry Dewey , Elena Reshetova , Carlos Bilbao , "Masami Hiramatsu (Google)" , Andrew Morton , Randy Dunlap , Petr Mladek , "Paul E. McKenney" , Eric DeVolder , =?UTF-8?q?Marc=20Aur=C3=A8le=20La=20France?= , "Gustavo A. R. Silva" , Nhat Pham , Greg Kroah-Hartman , "Christian Brauner (Microsoft)" , Douglas Anderson , Luis Chamberlain , Guenter Roeck , Mike Christie , Kent Overstreet , Maninder Singh , linux-doc@vger.kernel.org (open list:DOCUMENTATION), linux-kernel@vger.kernel.org (open list) Cc: Roberto Sassu , petr@tesarici.cz, Petr Tesarik Subject: [PATCH v1 3/5] sbm: call helpers and thunks Date: Wed, 14 Feb 2024 12:30:33 +0100 Message-Id: <20240214113035.2117-4-petrtesarik@huaweicloud.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240214113035.2117-1-petrtesarik@huaweicloud.com> References: <20240214113035.2117-1-petrtesarik@huaweicloud.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: LxC2BwAn0Rl8pMxlwo99Ag--.51308S5 X-Coremail-Antispam: 1UD129KBjvJXoW3Xw4rXrW8ZrWxKr17XFyrZwb_yoWfWFyDpF 18CFyDGFs8tFW2kr1Ska1vvr1ft3yUAr1UAF95J3s0y3yUtas5XFZ2kr4YyF9rAr48tFyf Xa1jqr1YgrnxArJanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUmS14x267AKxVWrJVCq3wAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2048vs2IY020E87I2jVAFwI0_JrWl82xGYIkIc2 x26xkF7I0E14v26ryj6s0DM28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wA2z4x0 Y4vE2Ix0cI8IcVAFwI0_Jr0_JF4l84ACjcxK6xIIjxv20xvEc7CjxVAFwI0_Gr1j6F4UJw A2z4x0Y4vEx4A2jsIE14v26r4j6F4UM28EF7xvwVC2z280aVCY1x0267AKxVW8Jr0_Cr1U M2AIxVAIcxkEcVAq07x20xvEncxIr21l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj6xIIjx v20xvE14v26r1j6r18McIj6I8E87Iv67AKxVWUJVW8JwAm72CE4IkC6x0Yz7v_Jr0_Gr1l F7xvr2IYc2Ij64vIr41lF7I21c0EjII2zVCS5cI20VAGYxC7M4IIrI8v6xkF7I0E8cxan2 IY04v7MxkF7I0Ew4C26cxK6c8Ij28IcwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE7xkE bVWUJVW8JwC20s026c02F40E14v26r1j6r18MI8I3I0E7480Y4vE14v26r106r1rMI8E67 AF67kF1VAFwI0_GFv_WrylIxkGc2Ij64vIr41lIxAIcVC0I7IYx2IY67AKxVWUJVWUCwCI 42IY6xIIjxv20xvEc7CjxVAFwI0_Gr1j6F4UJwCI42IY6xAIw20EY4v20xvaj40_Jr0_JF 4lIxAIcVC2z280aVAFwI0_Jr0_Gr1lIxAIcVC2z280aVCY1x0267AKxVW8Jr0_Cr1UYxBI daVFxhVjvjDU0xZFpf9x0JU7CzZUUUUU= X-CM-SenderInfo: hshw23xhvd2x3n6k3tpzhluzxrxghudrp/ Content-Type: text/plain; charset="utf-8" From: Petr Tesarik The sbm_exec() function allows to pass only a single void pointer to the target function running in sandbox mode. Provide a set of macros which make it easier to pass a wide variety of parameters from kernel mode to sandbox mode, preserving C type safety. To use this mechanism with a target function foo(), define the matching call helper and thunk like this: /* This can go into a header file: */ int foo(struct bar *data); SBM_DEFINE_CALL(foo, struct bar *, data); /* This should be defined together with foo(): */ SBM_DEFINE_THUNK(foo, struct bar *, data); The call helper, running in kernel mode, accepts the same set of parameters as the target function. It saves them in a target-specific struct and calls sbm_exec(), passing it a pointer to this struct. This pointer becomes the data parameter of the matching thunk function, running in sandbox mode. The thunk interprets the parameter as a pointer to the target-specific struct, loads the saved arguments from this struct and calls the target function. Define a shorthand macro SBM_DEFINE_FUNC() that can be used if the target function, thunk and call helper are all used only in one file: static SBM_DEFINE_FUNC(foo, struct bar *, data) { /* do something with data */ return 0; } Signed-off-by: Petr Tesarik --- include/linux/sbm.h | 208 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) diff --git a/include/linux/sbm.h b/include/linux/sbm.h index 9671b3c556c7..98fd27cd58d0 100644 --- a/include/linux/sbm.h +++ b/include/linux/sbm.h @@ -305,4 +305,212 @@ static inline int sbm_exec(struct sbm *sbm, sbm_func = func, void *data) =20 #endif /* CONFIG_SANDBOX_MODE */ =20 +/** + * __SBM_MAP() - Convert parameters to comma-separated expressions. + * @m: Macro used to convert each pair. + * @e: Expansion if no arguments are given. + */ +#define __SBM_MAP(m, e, ...) \ + CONCATENATE(__SBM_MAP, COUNT_ARGS(__VA_ARGS__))(m, e, ##__VA_ARGS__) +#define __SBM_MAP0(m, e) e +#define __SBM_MAP2(m, e, t, a) m(t, a) +#define __SBM_MAP4(m, e, t, a, ...) m(t, a), __SBM_MAP2(m, e, __VA_ARGS__) +#define __SBM_MAP6(m, e, t, a, ...) m(t, a), __SBM_MAP4(m, e, __VA_ARGS__) +#define __SBM_MAP8(m, e, t, a, ...) m(t, a), __SBM_MAP6(m, e, __VA_ARGS__) +#define __SBM_MAP10(m, e, t, a, ...) m(t, a), __SBM_MAP8(m, e, __VA_ARGS__) +#define __SBM_MAP12(m, e, t, a, ...) m(t, a), __SBM_MAP10(m, e, __VA_ARGS_= _) + +/** + * __SBM_MEMBERS() - Convert parameters to struct declaration body. + * + * This macro is similar to __SBM_MAP(), but the declarations are delimite= d by + * semicolons, not commas. + */ +#define __SBM_MEMBERS(...) \ + CONCATENATE(__SBM_MEMBERS, COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__) +#define __SBM_MEMBERS0() +#define __SBM_MEMBERS2(t, a) t a; +#define __SBM_MEMBERS4(t, a, ...) t a; __SBM_MEMBERS2(__VA_ARGS__) +#define __SBM_MEMBERS6(t, a, ...) t a; __SBM_MEMBERS4(__VA_ARGS__) +#define __SBM_MEMBERS8(t, a, ...) t a; __SBM_MEMBERS6(__VA_ARGS__) +#define __SBM_MEMBERS10(t, a, ...) t a; __SBM_MEMBERS8(__VA_ARGS__) +#define __SBM_MEMBERS12(t, a, ...) t a; __SBM_MEMBERS10(__VA_ARGS__) + +/************************* Target function **************************/ + +/** + * __SBM_DECL() - Map a parameter to a declaration. + * @type: Parameter type. + * @id: Parameter identifier. + * + * Use this macro with __SBM_MAP() to get variable or function parameter + * declarations. + */ +#define __SBM_DECL(type, id) type id + +/** + * __SBM_DECLARE_FUNC() - Declare a target function. + * @f: Target function name. + * @...: Parameters as type-identifier pairs. + * + * Target function parameters are specified as type-identifier pairs, some= what + * similar to SYSCALL_DEFINEn(). The function name @f is followed by up to= 6 + * type and identifier pairs, one for each parameter. The number of parame= ters + * is determined automatically. + * + * For example, if your target function is declared like this: + * + * .. code-block:: c + * static int foo(struct bar *baz); + * + * it would be declared with __SBM_DECLARE_FUNC() like this: + * + * .. code-block:: c + * static __SBM_DECLARE_FUNC(foo, struct bar *, baz); + * + */ +#define __SBM_DECLARE_FUNC(f, ...) \ + int f(__SBM_MAP(__SBM_DECL, void, ##__VA_ARGS__)) + +/*************************** Call helper ****************************/ + +/** + * __SBM_CALL() - Call helper function identifier. + * @f: Target function name. + */ +#define __SBM_CALL(f) __sbm_call_##f + +/** + * __SBM_VAR() - Map a parameter to its identifier. + * @type: Parameter type (unused). + * @id: Parameter identifier. + * + * Use this macro with __SBM_MAP() to get only the identifier from each + * type-identifier pair. + */ +#define __SBM_VAR(type, id) id + +/** + * __SBM_OPT_ARG() - Define an optional macro argument. + * @...: Optional parameters. + * + * Expand to a comma followed by all macro parameters, but if the parameter + * list is empty, expand to nothing (not even the comma). + */ +#define __SBM_OPT_ARG(...) __SBM_OPT_ARG_1(__VA_ARGS__) +#define __SBM_OPT_ARG_1(...) , ##__VA_ARGS__ + +/** + * SBM_DEFINE_CALL() - Define a call helper. + * @f: Target function name. + * @...: Parameters as type-identifier pairs. + * + * Declare an argument-passing struct and define the corresponding call + * helper. The call helper stores its arguments in an automatic variable of + * the corresponding type and calls sbm_exec(). + * + * The call helper is an inline function, so it is OK to use this macro in + * header files. + * + * Target function parameters are specified as type-identifier pairs, see + * __SBM_DECLARE_FUNC(). + */ +#define SBM_DEFINE_CALL(f, ...) \ + int __SBM_THUNK(f)(void *__p); \ + struct __SBM_ARG(f) { \ + __SBM_MEMBERS(__VA_ARGS__) \ + }; \ + static inline int __SBM_CALL(f)( \ + struct sbm *__sbm \ + __SBM_OPT_ARG(__SBM_MAP(__SBM_DECL, , ##__VA_ARGS__))) \ + { \ + struct __SBM_ARG(f) __args =3D { \ + __SBM_MAP(__SBM_VAR, , ##__VA_ARGS__) \ + }; \ + return sbm_exec(__sbm, __SBM_THUNK(f), &__args); \ + } + +/************************** Thunk function **************************/ + +/** + * __SBM_ARG() - Struct tag for target function arguments. + * @f: Target function name. + */ +#define __SBM_ARG(f) __sbm_arg_##f + +/** + * __SBM_DEREF() - Map a parameter to a struct __SBM_ARG() field. + * @type: Parameter type (unused). + * @id: Parameter identifier. + * + * Use this macro with __SBM_MAP() to dereference a struct __SBM_ARG() + * pointer. + */ +#define __SBM_DEREF(type, id) __arg->id + +/** + * __SBM_THUNK() - Thunk function identifier. + * @f: Target function name. + * + * Use this macro to generate the thunk function identifier for a given ta= rget + * function. + */ +#define __SBM_THUNK(f) __sbm_thunk_##f + +/** + * SBM_DEFINE_THUNK() - Define a thunk function. + * @f: Target function name. + * @...: Parameters as type-identifier pairs. + * + * The thunk function casts its parameter back to the argument-passing str= uct + * and calls the target function @f with parameters stored there by the ca= ll + * helper. + * + * Target function parameters are specified as type-identifier pairs, see + * __SBM_DECLARE_FUNC(). + */ +#define SBM_DEFINE_THUNK(f, ...) \ + int __SBM_THUNK(f)(void *__p) \ + { \ + struct __SBM_ARG(f) *__arg __maybe_unused =3D __p; \ + return (f)(__SBM_MAP(__SBM_DEREF, , ##__VA_ARGS__)); \ + } + +/**************************** Shorthands ****************************/ + +/** + * SBM_DEFINE_FUNC() - Define target function, thunk and call helper. + * @f: Target function name. + * @...: Parameters as type-identifier pairs. + * + * Declare or define a target function and also the corresponding + * thunk and call helper. Use this shorthand to avoid repeating the + * target function signature. + * + * The target function is declared twice. The first declaration allows to + * precede the macro with storage-class specifiers. The second declaration + * allows to follow the macro with the function body. You can also put a + * semicolon after the macro to make it only a declaration. + * + * Target function parameters are specified as type-identifier pairs, see + * __SBM_DECLARE_FUNC(). + */ +#define SBM_DEFINE_FUNC(f, ...) \ + __SBM_DECLARE_FUNC(f, ##__VA_ARGS__); \ + static SBM_DEFINE_CALL(f, ##__VA_ARGS__) \ + static SBM_DEFINE_THUNK(f, ##__VA_ARGS__) \ + __SBM_DECLARE_FUNC(f, ##__VA_ARGS__) + +/** + * sbm_call() - Call a function in sandbox mode. + * @sbm: SBM instance. + * @func: Function to be called. + * @...: Target function arguments. + * + * Call a function using a call helper which was previously defined with + * SBM_DEFINE_FUNC(). + */ +#define sbm_call(sbm, func, ...) \ + __SBM_CALL(func)(sbm, ##__VA_ARGS__) + #endif /* __LINUX_SBM_H */ --=20 2.34.1 From nobody Mon Feb 9 01:43:00 2026 Received: from frasgout13.his.huawei.com (frasgout13.his.huawei.com [14.137.139.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 55BAB1BDCF; Wed, 14 Feb 2024 11:32:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=14.137.139.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707910348; cv=none; b=ZOXk8tiyIedg6AENRLRh+fLOXc6EGios4Q8VWRo+N5hzwWtTA9TqKdCPHk7g0dGt33QSQqCCfvLoGic4ef+fDmAFrRpXU9SNG0huaPXgCkZPH9/z1joZ+jYOZs7Kf13aJ10bG97P3C37me60adjy5aD112rcGwsP0nvxeKhZXYI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707910348; c=relaxed/simple; bh=LbmnjlS4bxOqrvvrRtXPtPXLVGbYKpGLDnbDIyvZ+2U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=dch07I/rl/RrcBgGXQR2eQm8aaTXrGfO/xKVIxOEW6rPJGpJQt2SzGW5kDmgQeVwxqhM3yP/d9N86/fkz9LehUw1irIPWLApwjgSVKIUEhtT2PtBEjy+rIag7gDwp2J8O2j5oeHEgbIUc9bYwmjNOoOwDj1PfWlejk2ft3I1Vkg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com; spf=pass smtp.mailfrom=huaweicloud.com; arc=none smtp.client-ip=14.137.139.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huaweicloud.com Received: from mail.maildlp.com (unknown [172.18.186.51]) by frasgout13.his.huawei.com (SkyGuard) with ESMTP id 4TZbJ42hbcz9xsDX; Wed, 14 Feb 2024 19:17:08 +0800 (CST) Received: from mail02.huawei.com (unknown [7.182.16.47]) by mail.maildlp.com (Postfix) with ESMTP id 472F414059E; Wed, 14 Feb 2024 19:32:15 +0800 (CST) Received: from huaweicloud.com (unknown [10.45.156.69]) by APP1 (Coremail) with SMTP id LxC2BwAn0Rl8pMxlwo99Ag--.51308S6; Wed, 14 Feb 2024 12:32:14 +0100 (CET) From: Petr Tesarik To: Jonathan Corbet , David Kaplan , Larry Dewey , Elena Reshetova , Carlos Bilbao , "Masami Hiramatsu (Google)" , Andrew Morton , Randy Dunlap , Petr Mladek , "Paul E. McKenney" , Eric DeVolder , =?UTF-8?q?Marc=20Aur=C3=A8le=20La=20France?= , "Gustavo A. R. Silva" , Nhat Pham , Greg Kroah-Hartman , "Christian Brauner (Microsoft)" , Douglas Anderson , Luis Chamberlain , Guenter Roeck , Mike Christie , Kent Overstreet , Maninder Singh , linux-doc@vger.kernel.org (open list:DOCUMENTATION), linux-kernel@vger.kernel.org (open list) Cc: Roberto Sassu , petr@tesarici.cz, Petr Tesarik Subject: [PATCH v1 4/5] sbm: SandBox Mode KUnit test suite Date: Wed, 14 Feb 2024 12:30:34 +0100 Message-Id: <20240214113035.2117-5-petrtesarik@huaweicloud.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240214113035.2117-1-petrtesarik@huaweicloud.com> References: <20240214113035.2117-1-petrtesarik@huaweicloud.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: LxC2BwAn0Rl8pMxlwo99Ag--.51308S6 X-Coremail-Antispam: 1UD129KBjvAXoW3KF1UKF4kKF4kZrykCrWfXwb_yoW8ZrW7Ao WfXwnrtw48tw1xAaykJw1DKFW7Zr4DKFnxJwsxAr4qq3srtwn8JF17W345JF18JF15C3s7 X3WUJw47XrWktr1rn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UjIYCTnIWjp_UUUO-7AC8VAFwI0_Wr0E3s1l1xkIjI8I6I8E6xAIw20EY4v20xva j40_Wr0E3s1l1IIY67AEw4v_Jr0_Jr4l82xGYIkIc2x26280x7IE14v26r126s0DM28Irc Ia0xkI8VCY1x0267AKxVW5JVCq3wA2ocxC64kIII0Yj41l84x0c7CEw4AK67xGY2AK021l 84ACjcxK6xIIjxv20xvE14v26r1j6r1xM28EF7xvwVC0I7IYx2IY6xkF7I0E14v26r4UJV WxJr1l84ACjcxK6I8E87Iv67AKxVW8JVWxJwA2z4x0Y4vEx4A2jsIEc7CjxVAFwI0_Gr1j 6F4UJwAS0I0E0xvYzxvE52x082IY62kv0487Mc02F40EFcxC0VAKzVAqx4xG6I80ewAv7V C0I7IYx2IY67AKxVWUJVWUGwAv7VC2z280aVAFwI0_Jr0_Gr1lOx8S6xCaFVCjc4AY6r1j 6r4UM4x0Y48IcxkI7VAKI48JM4x0x7Aq67IIx4CEVc8vx2IErcIFxwACI402YVCY1x0262 8vn2kIc2xKxwCY1x0264kExVAvwVAq07x20xyl42xK82IYc2Ij64vIr41l4I8I3I0E4IkC 6x0Yz7v_Jr0_Gr1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x8GjcxK67AKxVWUGVWUWw C2zVAF1VAY17CE14v26r4a6rW5MIIYrxkI7VAKI48JMIIF0xvE2Ix0cI8IcVAFwI0_Jr0_ JF4lIxAIcVC0I7IYx2IY6xkF7I0E14v26r4UJVWxJr1lIxAIcVCF04k26cxKx2IYs7xG6r 1j6r1xMIIF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4A2jsIEc7CjxVAFwI0_Gr1j6F4U JbIYCTnIWIevJa73UjIFyTuYvjfU1WlkDUUUU X-CM-SenderInfo: hshw23xhvd2x3n6k3tpzhluzxrxghudrp/ Content-Type: text/plain; charset="utf-8" From: Petr Tesarik Test that a function called in sandbox mode can use input and output buffers but blocks attempts to read or write outside the pre-defined memory areas. Some tests intentionally access a non-present page, which generates a page fault in kernel space if SBM implementation cannot recover from failures. Theoretically, the page fault handler could recover from such page faults, but the exact details differ across architectures, and there is no universal method. The most portable approach is to let the page fault handler treat the fault as unrecoverable and kill the current task. This is why a child task is used to run these tests. The parent KUnit thread can check if the child was killed with a signal. Signed-off-by: Petr Tesarik --- kernel/Kconfig.sbm | 12 + kernel/Makefile | 1 + kernel/sbm_test.c | 735 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 748 insertions(+) create mode 100644 kernel/sbm_test.c diff --git a/kernel/Kconfig.sbm b/kernel/Kconfig.sbm index 64d683cefd4d..d28a754bc704 100644 --- a/kernel/Kconfig.sbm +++ b/kernel/Kconfig.sbm @@ -29,3 +29,15 @@ config SANDBOX_MODE and leaving the sandbox. =20 If unsure, say N. + +config SBM_KUNIT_TEST + tristate "Unit tests for SandBox Mode" if !KUNIT_ALL_TESTS + depends on KUNIT && SANDBOX_MODE + default KUNIT_ALL_TESTS + help + Build unit tests for SandBox Mode. + + For more information on KUnit and unit tests in general, please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. diff --git a/kernel/Makefile b/kernel/Makefile index ecc4bfd6213f..012df6fd817a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -116,6 +116,7 @@ obj-$(CONFIG_HAVE_STATIC_CALL_INLINE) +=3D static_call_= inline.o obj-$(CONFIG_CFI_CLANG) +=3D cfi.o obj-$(CONFIG_NUMA) +=3D numa.o obj-$(CONFIG_SANDBOX_MODE) +=3D sbm.o +obj-$(CONFIG_SBM_KUNIT_TEST) +=3D sbm_test.o =20 obj-$(CONFIG_PERF_EVENTS) +=3D events/ =20 diff --git a/kernel/sbm_test.c b/kernel/sbm_test.c new file mode 100644 index 000000000000..f3ad24ccf332 --- /dev/null +++ b/kernel/sbm_test.c @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023-2024 Huawei Technologies Duesseldorf GmbH + */ + +#include + +#include +#include +#include +#include +#include + +#define PASS 0x600d600d +#define FAIL 0xbad00bad + +struct data { + int value; +}; + +struct page_over { + union { + unsigned char page[PAGE_SIZE]; + struct data data; + }; + int nextpage; +}; + +struct thread_data { + const struct data *in; + struct data *out; +}; + +typedef int (*sbm_threadfn)(struct kunit *test, struct thread_data *data); + +struct thread_param { + struct kunit *test; + sbm_threadfn threadfn; + struct thread_data *data; + int err; +}; + +static void check_safe_sbm(struct kunit *test) +{ + if (!IS_ENABLED(CONFIG_HAVE_ARCH_SBM)) + kunit_skip(test, "requires arch hooks"); +} + +/************************************************************** + * Helpers to handle Oops in a sandbox mode kernel thread + * + * The kernel does not offer a general method for recovering + * from page faults in kernel mode. To intercept a planned + * page fault, let a helper thread oops and die. + */ + +static int call_sbm_threadfn(void *arg) +{ + struct thread_param *params =3D arg; + + params->err =3D params->threadfn(params->test, params->data); + do_exit(0); +} + +static int run_sbm_kthread(struct kunit *test, sbm_threadfn threadfn, + struct thread_data *data) +{ + struct thread_param params =3D { + .test =3D test, + .threadfn =3D threadfn, + .data =3D data, + }; + int pid, status; + + /* Do not let the child autoreap. */ + kernel_sigaction(SIGCHLD, SIG_DFL); + + pid =3D kernel_thread(call_sbm_threadfn, ¶ms, test->name, + CLONE_FS | CLONE_FILES | SIGCHLD); + KUNIT_ASSERT_GT(test, pid, 0); + KUNIT_ASSERT_EQ(test, kernel_wait(pid, &status), pid); + + /* Ignore SIGCHLD again. */ + kernel_sigaction(SIGCHLD, SIG_IGN); + + /* Killed by a signal? */ + if (status & 0x7f) + return -EFAULT; + + KUNIT_ASSERT_EQ(test, status, 0); + return params.err; +} + +/************************************************************** + * Simple write to output buffer. + * + * Verify that the output buffer is copied back to the caller. + */ + +static SBM_DEFINE_FUNC(write, struct data *, out) +{ + out->value =3D PASS; + return 0; +} + +static void sbm_write(struct kunit *test) +{ + struct sbm sbm; + struct data out; + int err; + + out.value =3D FAIL; + sbm_init(&sbm); + err =3D sbm_call(&sbm, write, + SBM_COPY_OUT(&sbm, &out, sizeof(out))); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, 0); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), 0); + KUNIT_EXPECT_EQ(test, out.value, PASS); +} + +/************************************************************** + * Memory write past output buffer within the same page. + * + * Writes beyond buffer end are ignored. + */ + +static SBM_DEFINE_FUNC(write_past, struct data *, out) +{ + out[0].value =3D PASS; + out[1].value =3D FAIL; + + return 0; +} + +static void sbm_write_past(struct kunit *test) +{ + struct sbm sbm; + struct data out[2]; + int err; + + out[0].value =3D FAIL; + out[1].value =3D PASS; + sbm_init(&sbm); + err =3D sbm_call(&sbm, write_past, + SBM_COPY_OUT(&sbm, &out[0], sizeof(out[0]))); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, 0); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), 0); + KUNIT_EXPECT_EQ(test, out[0].value, PASS); + KUNIT_EXPECT_EQ(test, out[1].value, PASS); +} + +/************************************************************** + * Memory write to next page past output buffer. + * + * There is a guard page after the output buffer. Writes to + * this guard page should generate a page fault. + */ + +static SBM_DEFINE_FUNC(write_past_page, struct data *, out) +{ + struct page_over *over =3D (struct page_over *)out; + + over->nextpage =3D FAIL; + barrier(); + out[0].value =3D FAIL; + return 0; +} + +static int sbm_write_past_page_threadfn(struct kunit *text, + struct thread_data *data) +{ + struct sbm sbm; + int err; + + sbm_init(&sbm); + err =3D sbm_call(&sbm, write_past_page, + SBM_COPY_OUT(&sbm, data->out, sizeof(*data->out))); + sbm_destroy(&sbm); + return err; +} + +static void sbm_write_past_page(struct kunit *test) +{ + struct page_over *over; + int err; + + over =3D kunit_kzalloc(test, sizeof(*over), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, over); + over->data.value =3D PASS; + over->nextpage =3D PASS; + if (IS_ENABLED(CONFIG_HAVE_ARCH_SBM)) { + struct sbm sbm; + + sbm_init(&sbm); + err =3D sbm_call(&sbm, write_past_page, + SBM_COPY_OUT(&sbm, &over->data, + sizeof(over->data))); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); + sbm_destroy(&sbm); + } else { + struct thread_data data; + + data.out =3D &over->data; + err =3D run_sbm_kthread(test, sbm_write_past_page_threadfn, + &data); + } + + KUNIT_EXPECT_EQ(test, err, -EFAULT); + KUNIT_EXPECT_EQ(test, over->data.value, PASS); + KUNIT_EXPECT_EQ(test, over->nextpage, PASS); +} + +/************************************************************** + * Memory write before output buffer. + * + * There is a guard page before the output buffer. Writes to + * this guard page should generate a page fault. + */ + +static SBM_DEFINE_FUNC(write_before, struct data *, out) +{ + out[-1].value =3D FAIL; + barrier(); + out[0].value =3D FAIL; + return 0; +} + +static int sbm_write_before_threadfn(struct kunit *test, + struct thread_data *data) +{ + struct sbm sbm; + int err; + + sbm_init(&sbm); + err =3D sbm_call(&sbm, write_before, + SBM_COPY_OUT(&sbm, data->out, sizeof(*data->out))); + sbm_destroy(&sbm); + return err; +} + +static void sbm_write_before(struct kunit *test) +{ + struct data out; + int err; + + out.value =3D PASS; + if (IS_ENABLED(CONFIG_HAVE_ARCH_SBM)) { + struct sbm sbm; + + sbm_init(&sbm); + err =3D sbm_call(&sbm, write_before, + SBM_COPY_OUT(&sbm, &out, sizeof(out))); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); + sbm_destroy(&sbm); + } else { + struct thread_data data; + + data.out =3D &out; + err =3D run_sbm_kthread(test, sbm_write_before_threadfn, &data); + } + + KUNIT_EXPECT_EQ(test, err, -EFAULT); + KUNIT_EXPECT_EQ(test, out.value, PASS); +} + +/************************************************************** + * Memory write to kernel static data. + * + * Sandbox mode cannot write to a kernel static data. + */ + +struct data static_data =3D { .value =3D FAIL }; + +static void sbm_write_static(struct kunit *test) +{ + struct sbm sbm; + int err; + + check_safe_sbm(test); + + sbm_init(&sbm); + err =3D sbm_call(&sbm, write, &static_data); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, -EFAULT); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); + KUNIT_EXPECT_EQ(test, static_data.value, FAIL); +} + +/************************************************************** + * Memory write to kernel BSS. + * + * Sandbox mode cannot write to kernel BSS. + */ + +struct data static_bss; + +static void sbm_write_bss(struct kunit *test) +{ + struct sbm sbm; + int err; + + check_safe_sbm(test); + + static_bss.value =3D FAIL; + sbm_init(&sbm); + err =3D sbm_call(&sbm, write, &static_bss); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, -EFAULT); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); + KUNIT_EXPECT_EQ(test, static_bss.value, FAIL); +} + +/************************************************************** + * Memory write to unrelated buffer. + * + * Sandbox mode cannot write to the wrong buffer. + */ + +static void sbm_write_wrong(struct kunit *test) +{ + struct data *out; + struct sbm sbm; + int err; + + check_safe_sbm(test); + + out =3D kunit_kzalloc(test, sizeof(*out), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, out); + out->value =3D FAIL; + sbm_init(&sbm); + err =3D sbm_call(&sbm, write, out); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, -EFAULT); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); + KUNIT_EXPECT_EQ(test, out->value, FAIL); +} + +/************************************************************** + * Memory write to kernel stack. + * + * Sandbox mode runs on its own stack. The kernel stack cannot + * be modified. + */ + +static void sbm_write_stack(struct kunit *test) +{ + struct data out; + struct sbm sbm; + int err; + + check_safe_sbm(test); + + out.value =3D FAIL; + sbm_init(&sbm); + err =3D sbm_call(&sbm, write, &out); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, -EFAULT); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); + KUNIT_EXPECT_EQ(test, out.value, FAIL); +} + +/************************************************************** + * Simple update of an input/output buffer. + * + * Verify that the input buffer is copied in and also back to the caller. + */ + +static SBM_DEFINE_FUNC(update, struct data *, data) +{ + data->value ^=3D FAIL ^ PASS; + return 0; +} + +static void sbm_update(struct kunit *test) +{ + struct sbm sbm; + struct data data; + int err; + + data.value =3D FAIL; + sbm_init(&sbm); + err =3D sbm_call(&sbm, update, + SBM_COPY_INOUT(&sbm, &data, sizeof(data))); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, 0); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), 0); + KUNIT_EXPECT_EQ(test, data.value, PASS); +} + +/************************************************************** + * Copy from input buffer to output buffer. + * + * Verify that sandbox mode can read from the input buffer and + * write to the output buffer. + */ + +static int copy_value(const struct data *in, struct data *out) +{ + out->value =3D in->value; + return 0; +} + +/* + * Define call helper and thunk explicitly to verify that this syntax also + * works. + */ +static SBM_DEFINE_CALL(copy_value, const struct data *, in, + struct data *, out); +static SBM_DEFINE_THUNK(copy_value, const struct data *, in, + struct data *, out); + +static void sbm_copy_value(struct kunit *test) +{ + struct sbm sbm; + struct data in[1]; + struct data out[1]; + int err; + + in[0].value =3D PASS; + out[0].value =3D FAIL; + sbm_init(&sbm); + err =3D sbm_call(&sbm, copy_value, + SBM_COPY_IN(&sbm, in, sizeof(in)), + SBM_COPY_OUT(&sbm, out, sizeof(out))); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, 0); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), 0); + KUNIT_EXPECT_EQ(test, out[0].value, PASS); +} + +/************************************************************** + * Memory read past input buffer within the same page. + * + * The page beyond the input buffer is initialized to zero. + */ + +static SBM_DEFINE_FUNC(read_past, const struct data *, in) +{ + return in[1].value; +} + +static void sbm_read_past(struct kunit *test) +{ + struct sbm sbm; + struct data in[2]; + int err; + + in[0].value =3D PASS; + in[1].value =3D FAIL; + sbm_init(&sbm); + err =3D sbm_call(&sbm, read_past, + SBM_COPY_IN(&sbm, &in[0], sizeof(in[0]))); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, 0); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), 0); +} + +/************************************************************** + * Memory read from next page past input buffer. + * + * There is a guard page after the input buffer. Reading from + * that page should generate a page fault. + */ + +static SBM_DEFINE_FUNC(read_past_page, const struct data *, in) +{ + const struct page_over *over =3D (const struct page_over *)in; + + return over->nextpage; +} + +static int sbm_read_past_page_threadfn(struct kunit *test, + struct thread_data *data) +{ + struct sbm sbm; + int err; + + sbm_init(&sbm); + err =3D sbm_call(&sbm, read_past_page, + SBM_COPY_IN(&sbm, data->in, sizeof(*data->in))); + sbm_destroy(&sbm); + return err; +} + +static void sbm_read_past_page(struct kunit *test) +{ + struct page_over *over; + int err; + + over =3D kunit_kzalloc(test, sizeof(*over), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, over); + over->data.value =3D PASS; + over->nextpage =3D FAIL; + if (IS_ENABLED(CONFIG_HAVE_ARCH_SBM)) { + struct sbm sbm; + + sbm_init(&sbm); + err =3D sbm_call(&sbm, read_past_page, + SBM_COPY_IN(&sbm, &over->data, + sizeof(over->data))); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); + sbm_destroy(&sbm); + } else { + struct thread_data data; + + data.in =3D &over->data; + err =3D run_sbm_kthread(test, sbm_read_past_page_threadfn, &data); + } + + KUNIT_EXPECT_EQ(test, err, -EFAULT); +} + +/************************************************************** + * Memory read before input buffer. + * + * There is a guard page before the input buffer. Reading from + * that page should generate a page fault. + */ + +static SBM_DEFINE_FUNC(read_before, const struct data *, in) +{ + return in[-1].value; +} + +static int sbm_read_before_threadfn(struct kunit *test, + struct thread_data *data) +{ + struct sbm sbm; + int err; + + sbm_init(&sbm); + err =3D sbm_call(&sbm, read_before, + SBM_COPY_IN(&sbm, data->in, sizeof(*data->in))); + sbm_destroy(&sbm); + return err; +} + +static void sbm_read_before(struct kunit *test) +{ + struct data in; + int err; + + in.value =3D PASS; + if (IS_ENABLED(CONFIG_HAVE_ARCH_SBM)) { + struct sbm sbm; + + sbm_init(&sbm); + err =3D sbm_call(&sbm, read_before, + SBM_COPY_IN(&sbm, &in, sizeof(in))); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); + sbm_destroy(&sbm); + } else { + struct thread_data data; + + data.in =3D ∈ + err =3D run_sbm_kthread(test, sbm_read_before_threadfn, &data); + } + + KUNIT_EXPECT_EQ(test, err, -EFAULT); +} + +/************************************************************** + * Memory read from unrelated buffer. + * + * Sandbox mode cannot read from the wrong buffer. + */ + +static SBM_DEFINE_FUNC(read_wrong, const struct data *, in) +{ + return in->value; +} + +static void sbm_read_wrong(struct kunit *test) +{ + struct data *in; + struct sbm sbm; + int err; + + check_safe_sbm(test); + + in =3D kunit_kzalloc(test, sizeof(*in), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, in); + in->value =3D FAIL; + sbm_init(&sbm); + err =3D sbm_call(&sbm, read_wrong, in); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, -EFAULT); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); +} + +/************************************************************** + * Stack bottom. + * + * Sandbox mode can read from the bottom of the kernel stack. + * Verify that all of the kernel stack is mapped. + */ + +static SBM_DEFINE_FUNC(stack_bottom) +{ + return *end_of_stack(current); +} + +static void sbm_stack_bottom(struct kunit *test) +{ + struct sbm sbm; + unsigned long *bottom; + int err; + + bottom =3D end_of_stack(current); + *bottom =3D PASS; + sbm_init(&sbm); + err =3D sbm_call(&sbm, stack_bottom); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, PASS); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), 0); + KUNIT_EXPECT_EQ(test, *bottom, PASS); +} + +/************************************************************** + * Stack overflow. + * + * Sandbox mode cannot overflow the stack. + * + * This test is not safe without SBM arch hooks, because the kernel may pa= nic + * when kernel stack overflow is detected. + */ + +static noinline int kaboom(void) +{ + return 0; +} + +static SBM_DEFINE_FUNC(stack_overflow) +{ + unsigned long old_sp =3D current_stack_pointer; + int err; + + current_stack_pointer =3D (unsigned long)end_of_stack(current); + barrier(); + err =3D kaboom(); + current_stack_pointer =3D old_sp; + return err; +} + +static void sbm_stack_overflow(struct kunit *test) +{ + struct sbm sbm; + int err; + + check_safe_sbm(test); + + sbm_init(&sbm); + err =3D sbm_call(&sbm, stack_overflow); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, -EFAULT); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); +} + +#ifdef CONFIG_X86_64 + +/************************************************************** + * [X86-64] Non-canonical address. + * + * Sandbox mode recovers from a #GP exception. Test it by + * memory write to a non-canonical address. + */ + +static void sbm_x86_64_gp(struct kunit *test) +{ + void *non_canonical; + struct sbm sbm; + int err; + + check_safe_sbm(test); + + non_canonical =3D (void *)(1ULL << 63); + sbm_init(&sbm); + err =3D sbm_call(&sbm, write, non_canonical); + sbm_destroy(&sbm); + + KUNIT_EXPECT_EQ(test, err, -EFAULT); + KUNIT_EXPECT_EQ(test, sbm_error(&sbm), -EFAULT); +} + +#endif + +/************************************************************** + * Test suite metadata. + */ + +static struct kunit_case sbm_test_cases[] =3D { + KUNIT_CASE(sbm_write), + KUNIT_CASE(sbm_write_past), + KUNIT_CASE(sbm_write_past_page), + KUNIT_CASE(sbm_write_before), + KUNIT_CASE(sbm_write_static), + KUNIT_CASE(sbm_write_bss), + KUNIT_CASE(sbm_write_wrong), + KUNIT_CASE(sbm_write_stack), + KUNIT_CASE(sbm_copy_value), + KUNIT_CASE(sbm_read_past), + KUNIT_CASE(sbm_read_past_page), + KUNIT_CASE(sbm_read_before), + KUNIT_CASE(sbm_read_wrong), + KUNIT_CASE(sbm_update), + KUNIT_CASE(sbm_stack_bottom), + KUNIT_CASE(sbm_stack_overflow), +#ifdef CONFIG_X86_64 + KUNIT_CASE(sbm_x86_64_gp), +#endif + {} +}; + +static struct kunit_suite sbm_test_suite =3D { + .name =3D "sandbox_mode", + .test_cases =3D sbm_test_cases, +}; + +kunit_test_suites(&sbm_test_suite); + +MODULE_LICENSE("GPL"); --=20 2.34.1 From nobody Mon Feb 9 01:43:00 2026 Received: from frasgout12.his.huawei.com (frasgout12.his.huawei.com [14.137.139.154]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D15801C693; Wed, 14 Feb 2024 11:32:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=14.137.139.154 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707910363; cv=none; b=QE6fpR9/mDQL7qMB+75JUigOEoq8EMo9cJuUJCWHVUeNVmlmFo6R2bNCH8dl28nZSY4ir5F7AllW8DF3mzfIaDeMawkLXmiynFwO4CIapZSlVBxgynlpsM6QmMM9FUQZd2jN3wF0V70NxkhjlIQ9izYNX4VBb9eY/k5VumEA3Bo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707910363; c=relaxed/simple; bh=upDj9qpTtCStajMBNXnNGiR+ZhJ3V3cjJpEDu8+zmq4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Zleptk5j0SvwkcCuU19HKb1WDBnUtaknK8f+HI2lbtSPCrvNVX6HMfgWiKQhWWpNV1eyG8OHuO3SDwEdQ4UT3Gm7qKhavDf24h2IFOU6siYUC16ktKf8vZbeSivGUJf3V3JvTGvsCjK7GtsGEbSqhnC1I6z/HBtomipcM/hvFNk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com; spf=pass smtp.mailfrom=huaweicloud.com; arc=none smtp.client-ip=14.137.139.154 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=huaweicloud.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huaweicloud.com Received: from mail.maildlp.com (unknown [172.18.186.51]) by frasgout12.his.huawei.com (SkyGuard) with ESMTP id 4TZbCt6kvYz9y62R; Wed, 14 Feb 2024 19:13:30 +0800 (CST) Received: from mail02.huawei.com (unknown [7.182.16.47]) by mail.maildlp.com (Postfix) with ESMTP id DEE94140668; Wed, 14 Feb 2024 19:32:27 +0800 (CST) Received: from huaweicloud.com (unknown [10.45.156.69]) by APP1 (Coremail) with SMTP id LxC2BwAn0Rl8pMxlwo99Ag--.51308S7; Wed, 14 Feb 2024 12:32:27 +0100 (CET) From: Petr Tesarik To: Jonathan Corbet , David Kaplan , Larry Dewey , Elena Reshetova , Carlos Bilbao , "Masami Hiramatsu (Google)" , Andrew Morton , Randy Dunlap , Petr Mladek , "Paul E. McKenney" , Eric DeVolder , =?UTF-8?q?Marc=20Aur=C3=A8le=20La=20France?= , "Gustavo A. R. Silva" , Nhat Pham , Greg Kroah-Hartman , "Christian Brauner (Microsoft)" , Douglas Anderson , Luis Chamberlain , Guenter Roeck , Mike Christie , Kent Overstreet , Maninder Singh , linux-doc@vger.kernel.org (open list:DOCUMENTATION), linux-kernel@vger.kernel.org (open list) Cc: Roberto Sassu , petr@tesarici.cz, Petr Tesarik Subject: [PATCH v1 5/5] sbm: SandBox Mode documentation Date: Wed, 14 Feb 2024 12:30:35 +0100 Message-Id: <20240214113035.2117-6-petrtesarik@huaweicloud.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240214113035.2117-1-petrtesarik@huaweicloud.com> References: <20240214113035.2117-1-petrtesarik@huaweicloud.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: LxC2BwAn0Rl8pMxlwo99Ag--.51308S7 X-Coremail-Antispam: 1UD129KBjvJXoW3Xw4rJryrtw1DCryxWw43Awb_yoW3uw4fpF Zxta4ftF4DJFy7Zr1xJw4xZFyFyw4rAr45KF95G34Fvas0934vyF1Fqr18uFy7CrWkCa4j qF4jgr1UCwn8A37anT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUmm14x267AKxVWrJVCq3wAFc2x0x2IEx4CE42xK8VAvwI8IcIk0 rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2048vs2IY020E87I2jVAFwI0_JF0E3s1l82xGYI kIc2x26xkF7I0E14v26ryj6s0DM28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48ve4kI8wA2 z4x0Y4vE2Ix0cI8IcVAFwI0_JFI_Gr1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI0_Gr1j6F 4UJwA2z4x0Y4vEx4A2jsIE14v26r4j6F4UM28EF7xvwVC2z280aVCY1x0267AKxVW8Jr0_ Cr1UM2AIxVAIcxkEcVAq07x20xvEncxIr21l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj6x IIjxv20xvE14v26r1j6r18McIj6I8E87Iv67AKxVWUJVW8JwAm72CE4IkC6x0Yz7v_Jr0_ Gr1lF7xvr2IYc2Ij64vIr41lF7I21c0EjII2zVCS5cI20VAGYxC7M4IIrI8v6xkF7I0E8c xan2IY04v7MxkF7I0Ew4C26cxK6c8Ij28IcwCF04k20xvY0x0EwIxGrwCFx2IqxVCFs4IE 7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18MI8I3I0E7480Y4vE14v26r106r1rMI 8E67AF67kF1VAFwI0_GFv_WrylIxkGc2Ij64vIr41lIxAIcVC0I7IYx2IY67AKxVWUCVW8 JwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr1j6F4UJwCI42IY6xAIw20EY4v20xvaj40_Jr 0_JF4lIxAIcVC2z280aVAFwI0_Jr0_Gr1lIxAIcVC2z280aVCY1x0267AKxVW8Jr0_Cr1U YxBIdaVFxhVjvjDU0xZFpf9x0JUzwZcUUUUU= X-CM-SenderInfo: hshw23xhvd2x3n6k3tpzhluzxrxghudrp/ Content-Type: text/plain; charset="utf-8" From: Petr Tesarik Add a SandBox Mode document under Documentation/security. Describe the concept, usage and known limitations. Signed-off-by: Petr Tesarik --- Documentation/security/index.rst | 1 + Documentation/security/sandbox-mode.rst | 180 ++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 Documentation/security/sandbox-mode.rst diff --git a/Documentation/security/index.rst b/Documentation/security/inde= x.rst index 59f8fc106cb0..680a0b8bf28b 100644 --- a/Documentation/security/index.rst +++ b/Documentation/security/index.rst @@ -14,6 +14,7 @@ Security Documentation sak SCTP self-protection + sandbox-mode siphash tpm/index digsig diff --git a/Documentation/security/sandbox-mode.rst b/Documentation/securi= ty/sandbox-mode.rst new file mode 100644 index 000000000000..4405b8858c4a --- /dev/null +++ b/Documentation/security/sandbox-mode.rst @@ -0,0 +1,180 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +SandBox Mode +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Introduction +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The primary goal of SandBox Mode (SBM) is to reduce the impact of potential +memory safety bugs in kernel code by decomposing the kernel. The SBM API +allows to run each component inside an isolated execution environment. In +particular, memory areas used as input and/or output are isolated from the +rest of the kernel and surrounded by guard pages. Without arch hooks, this +common base provides *weak isolation*. + +On architectures which implement the necessary arch hooks, SandBox Mode +leverages hardware paging facilities and CPU privilege levels to enforce t= he +use of only these predefined memory areas. With arch support, SBM can also +recover from protection violations. This means that SBM forcibly terminates +the sandbox and returns an error code (e.g. ``-EFAULT``) to the caller, so +execution can continue. Such implementation provides *strong isolation*. + +A target function in a sandbox communicates with the rest of the kernel +through a caller-defined interface, comprising read-only buffers (input), +read-write buffers (output) and the return value. The caller can explicitly +share other data with the sandbox, but doing so may reduce isolation stren= gth. + +Protection of sensitive kernel data is currently out of scope. SandBox Mod= e is +meant to run kernel code which would otherwise have full access to all sys= tem +resources. SBM allows to impose a scoped access control policy on which +resources are available to the sandbox. That said, protection of sensitive +data is foreseen as a future goal, and that's why the API is designed to +control not only memory writes but also memory reads. + +The expected use case for SandBox Mode is parsing data from untrusted sour= ces, +especially if the parsing cannot be reasonably done by a user mode helper. +Keep in mind that a sandbox doesn't guarantee that the output data is corr= ect. +The result may be corrupt (e.g. as a result of an exploited bug) and where +applicable, it should be sanitized before further use. + +Using SandBox Mode +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +SandBox Mode is an optional feature, enabled with ``CONFIG_SANDBOX_MODE``. +However, the SBM API is always defined regardless of the kernel configurat= ion. +It will call a function with the best available isolation, which is: + +* *strong isolation* if both ``CONFIG_SANDBOX_MODE`` and + ``CONFIG_ARCH_HAVE_SBM`` are set, +* *weak isolation* if ``CONFIG_SANDBOX_MODE`` is set, but + ``CONFIG_ARCH_HAVE_SBM`` is unset, +* *no isolation* if ``CONFIG_SANDBOX_MODE`` is unset. + +Code which cannot safely run with no isolation should depend on the releva= nt +config option(s). + +The API can be used like this: + +.. code-block:: c + + #include + + /* Function to be executed in a sandbox. */ + static SBM_DEFINE_FUNC(my_func, const struct my_input *, in, + struct my_output *, out) + { + /* Read from in, write to out. */ + return 0; + } + + int caller(...) + { + /* Declare a SBM instance. */ + struct sbm sbm; + + /* Initialize SBM instance. */ + sbm_init(&sbm); + + /* Execute my_func() using the SBM instance. */ + err =3D sbm_call(&sbm, my_func, + SBM_COPY_IN(&sbm, input, in_size), + SBM_COPY_OUT(&sbm, output, out_size)); + + /* Clean up. */ + sbm_destroy(&sbm); + +The return type of a sandbox mode function is always ``int``. The return v= alue +is zero on success and negative on error. That's because the SBM helpers +return an error code (such as ``-ENOMEM``) if the call cannot be performed. + +If sbm_call() returns an error, you can use sbm_error() to decide whether = the +error was returned by the target function or because sandbox mode was abor= ted +(or failed to run entirely). + +Public API +---------- + +.. kernel-doc:: include/linux/sbm.h + :identifiers: sbm sbm_init sbm_destroy sbm_exec sbm_error + SBM_COPY_IN SBM_COPY_OUT SBM_COPY_INOUT + SBM_DEFINE_CALL SBM_DEFINE_THUNK SBM_DEFINE_FUNC + sbm_call + +Arch Hooks +---------- + +These hooks must be implemented to select HAVE_ARCH_SBM. + +.. kernel-doc:: include/linux/sbm.h + :identifiers: arch_sbm_init arch_sbm_destroy arch_sbm_exec + arch_sbm_map_readonly arch_sbm_map_writable + +Current Limitations +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +This section lists know limitations of the current SBM implementation, whi= ch +are planned to be removed in the future. + +Stack +----- + +There is no generic kernel API to run a function on an alternate stack, so= SBM +runs on the normal kernel stack by default. The kernel already offers +self-protection against stack overflows and underflows as well as against +overwriting on-stack data outside the current frame, but violations are +usually fatal. + +This limitation can be solved for specific targets. Arch hooks can set up a +separate stack and recover from stack frame overruns. + +Inherent Limitations +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +This section lists limitations which are inherent to the concept. + +Explicit Code +------------- + +The main idea behind SandBox Mode is decomposition of one big program (the +Linux kernel) into multiple smaller programs that can be sandboxed. AFAIK +there is no way to automate this task for an existing code base in C. + +Given the performance impact of running code in a sandbox, this limitation= may +be perceived as a benefit. It is expected that sandbox mode is introduced = only +knowingly and only where safety is more important than performance. + +Complex Data +------------ + +Although data structures are not serialized and deserialized between kernel +mode and sandbox mode, all directly and indirectly referenced data structu= res +must be explicitly mapped into the sandbox, which requires some manual eff= ort. + +Copying of input/output buffers also incurs some runtime overhead. This +overhead can be reduced by sharing data directly with the sandbox, but the +resulting isolation is weaker, so it may or may not be acceptable, dependi= ng +on the overall safety requirements. + +Page Granularity +---------------- + +Since paging is used to enforce memory safety, page size is the smallest u= nit. +Objects mapped into the sandbox must be aligned to a page boundary, and bu= ffer +overflows may not be detected if they fit into the same page. + +On the other hand, even though such writes are not detected, they do not +corrupt kernel data, because only the output buffer is copied back to kern= el +mode, and the (corrupted) rest of the page is ignored. + +Transitions +----------- + +Transitions between kernel mode and sandbox mode are synchronous. That is, +whenever entering or leaving sandbox mode, the currently running CPU execu= tes +the instructions necessary to save/restore its kernel-mode state. The API = is +generic enough to allow asynchronous transitions, e.g. to pass data to ano= ther +CPU which is already running in sandbox mode. However, to see the benefits= , a +hypothetical implementation would require far-reaching changes in the kern= el +scheduler. This is (currently) out of scope. --=20 2.34.1