From patchwork Thu Sep 5 14:56:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aleksa Sarai X-Patchwork-Id: 13792504 Received: from mout-p-103.mailbox.org (mout-p-103.mailbox.org [80.241.56.161]) (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 E9FFF19EEB1; Thu, 5 Sep 2024 14:57:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.161 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548241; cv=none; b=QHW3y1m8ztzSVwI6Dep0szwGvmfPK9qMclJMIu3lGsNt4uWQgBo+KMdVHXf48vkgnbB1iETM/it+M+PEtG74CjVaeItzHzDkTaoz26rbmbFH4NP7PT9sxjOI7TeHxJOdjWUMxdWAPbbr5Oic9PpGBYxTAIhkm8aVuaR7SNlkd4k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548241; c=relaxed/simple; bh=RHuQH37fa1aCHevS8Rp4DmyNPF3QBfkxOH5ERfeCQRY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LbJ9D8lfaTT9WEtVvzdQY+n11paDy175lLCjz+oMG1zob5C+A0kUojUI7tNsEvwqJrqNOGTdlKD3Tw1aOwAvqHNhzXKi6vKEBi/0+klA3tJd0abZHBiDOJT+WVz/zZh/ICXN7w67vPidp6jdfG4Oy3MMvXlhzJlgCCYVkhgqN14= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com; spf=pass smtp.mailfrom=cyphar.com; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b=SofAB/7g; arc=none smtp.client-ip=80.241.56.161 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cyphar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b="SofAB/7g" Received: from smtp102.mailbox.org (smtp102.mailbox.org [IPv6:2001:67c:2050:b231:465::102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-103.mailbox.org (Postfix) with ESMTPS id 4X02Wv31gXz9smK; Thu, 5 Sep 2024 16:57:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cyphar.com; s=MBO0001; t=1725548235; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=35gDDvLQFm6Wi82gl7IXq7L8XXDc6npzVovXGFIyqFM=; b=SofAB/7gxxU1YrFa4YK+T0B8nA7QFBCSaKHdW1RLXhYluBbY2qGvagTCD6BQ9+k1ngmiZk d6/ujWKvQTSZHhovZrBoEaV6p/MrXS6HW36pRke7l06oW55c3zRwIysY08OY/ayzo+M1JW gj8SSJEa+v1WfGbGTJo2VaHNokP+PIMimzemBvV9PV20WdWjOnqw04WJH2LZ4gDmGd4wD4 aKy0RUZsjsBfV5gKLGvIdgKN9/ivkV0pAiA/YaaGF2z/ZHmCs1QyfaAYr1xdi3e5gg6C0R oEw6bjHdhJJeLxOcaCR2+JMiZayT+YOuVrUyfrBWQi8eqs66Qdkh+5v5YZ+YGA== From: Aleksa Sarai Date: Fri, 06 Sep 2024 00:56:33 +1000 Subject: [PATCH RFC v2 01/10] uaccess: add copy_struct_to_user helper Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240906-extensible-structs-check_fields-v2-1-0f46d2de9bad@cyphar.com> References: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> In-Reply-To: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> To: Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Alexander Viro , Christian Brauner , Jan Kara , Arnd Bergmann , Shuah Khan Cc: Kees Cook , Florian Weimer , Arnd Bergmann , Mark Rutland , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, Aleksa Sarai X-Developer-Signature: v=1; a=openpgp-sha256; l=7231; i=cyphar@cyphar.com; h=from:subject:message-id; bh=RHuQH37fa1aCHevS8Rp4DmyNPF3QBfkxOH5ERfeCQRY=; b=owGbwMvMwCWmMf3Xpe0vXfIZT6slMaTdPLa5ZbFuyBb7B0+jO6M0aqXjD+deP2fecfLNabdj9 6ymblyg31HKwiDGxSArpsiyzc8zdNP8xVeSP61kg5nDygQyhIGLUwAmcqiJkeGJ5Mw3Vf/fWc5u uuC7j630YHZU+hOLlACWPYs1I2PVlq1n+MXE2SIVr/zrSWri1OtBQisC40TuHloh8FYsVGjezB5 Jfz4A X-Developer-Key: i=cyphar@cyphar.com; a=openpgp; fpr=C9C370B246B09F6DBCFC744C34401015D1D2D386 X-Rspamd-Queue-Id: 4X02Wv31gXz9smK This is based on copy_struct_from_user(), but there is one additional case to consider when creating a syscall that returns an extensible-struct to userspace -- how should data in the struct that cannot fit into the userspace struct be handled (ksize > usize)? There are three possibilies: 1. The interface is like sched_getattr(2), where new information will be silently not provided to userspace. This is probably what most interfaces will want to do, as it provides the most possible backwards-compatibility. 2. The interface is like lsm_list_modules(2), where you want to return an error like -EMSGSIZE if not providing information could result in the userspace program making a serious mistake (such as one that could lead to a security problem) or if you want to provide some flag to userspace so they know that they are missing some information. 3. The interface is like statx(2), where there some kind of a request mask that indicates what data userspace would like. One could imagine that statx2(2) (using extensible structs) would want to return -EMSGSIZE if the user explicitly requested a field that their structure is too small to fit, but not return an error if the field was not explicitly requested. This is kind of a mix between (1) and (2) based on the requested mask. The copy_struct_to_user() helper includes a an extra argument that is used to return a boolean flag indicating whether there was a non-zero byte in the trailing bytes that were not copied to userspace. This can be used in the following ways to handle all three cases, respectively: 1. Just pass NULL, as you don't care about this case. 2. Return an error (say -EMSGSIZE) if the argument was set to true by copy_struct_to_user(). 3. If the argument was set to true by copy_struct_to_user(), check if there is a flag that implies a field larger than usize. This is the only case where callers of copy_struct_to_user() should check usize themselves. This will probably require scanning an array that specifies what flags were added for each version of the flags struct and returning an error if the request mask matches any of the flags that were added in versions of the struct that are larger than usize. At the moment we don't have any users of (3), so this patch doesn't include any helpers to make the necessary scanning easier, but it should be fairly easy to add some if necessary. Signed-off-by: Aleksa Sarai --- include/linux/uaccess.h | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index d8e4105a2f21..5d0a590ef65d 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -387,6 +387,104 @@ copy_struct_from_user(void *dst, size_t ksize, const void __user *src, return 0; } +/** + * copy_struct_to_user: copy a struct to userspace + * @dst: Destination address, in userspace. This buffer must be @ksize + * bytes long. + * @usize: (Alleged) size of @dst struct. + * @src: Source address, in kernel space. + * @ksize: Size of @src struct. + * @ignored_trailing: Set to %true if there was a non-zero byte in @src that + * userspace cannot see because they are using an smaller struct. + * + * Copies a struct from kernel space to userspace, in a way that guarantees + * backwards-compatibility for struct syscall arguments (as long as future + * struct extensions are made such that all new fields are *appended* to the + * old struct, and zeroed-out new fields have the same meaning as the old + * struct). + * + * Some syscalls may wish to make sure that userspace knows about everything in + * the struct, and if there is a non-zero value that userspce doesn't know + * about, they want to return an error (such as -EMSGSIZE) or have some other + * fallback (such as adding a "you're missing some information" flag). If + * @ignored_trailing is non-%NULL, it will be set to %true if there was a + * non-zero byte that could not be copied to userspace (ie. was past @usize). + * + * While unconditionally returning an error in this case is the simplest + * solution, for maximum backward compatibility you should try to only return + * -EMSGSIZE if the user explicitly requested the data that couldn't be copied. + * Note that structure sizes can change due to header changes and simple + * recompilations without code changes(!), so if you care about + * @ignored_trailing you probably want to make sure that any new field data is + * associated with a flag. Otherwise you might assume that a program knows + * about data it does not. + * + * @ksize is just sizeof(*src), and @usize should've been passed by userspace. + * The recommended usage is something like the following: + * + * SYSCALL_DEFINE2(foobar, struct foo __user *, uarg, size_t, usize) + * { + * int err; + * bool ignored_trailing; + * struct foo karg = {}; + * + * if (usize > PAGE_SIZE) + * return -E2BIG; + * if (usize < FOO_SIZE_VER0) + * return -EINVAL; + * + * // ... modify karg somehow ... + * + * err = copy_struct_to_user(uarg, usize, &karg, sizeof(karg), + * &ignored_trailing); + * if (err) + * return err; + * if (ignored_trailing) + * return -EMSGSIZE: + * + * // ... + * } + * + * There are three cases to consider: + * * If @usize == @ksize, then it's copied verbatim. + * * If @usize < @ksize, then the kernel is trying to pass userspace a newer + * struct than it supports. Thus we only copy the interoperable portions + * (@usize) and ignore the rest (but @ignored_trailing is set to %true if + * any of the trailing (@ksize - @usize) bytes are non-zero). + * * If @usize > @ksize, then the kernel is trying to pass userspace an older + * struct than userspace supports. In order to make sure the + * unknown-to-the-kernel fields don't contain garbage values, we zero the + * trailing (@usize - @ksize) bytes. + * + * Returns (in all cases, some data may have been copied): + * * -EFAULT: access to userspace failed. + */ +static __always_inline __must_check int +copy_struct_to_user(void __user *dst, size_t usize, const void *src, + size_t ksize, bool *ignored_trailing) +{ + size_t size = min(ksize, usize); + size_t rest = max(ksize, usize) - size; + + /* Double check if ksize is larger than a known object size. */ + if (WARN_ON_ONCE(ksize > __builtin_object_size(src, 1))) + return -E2BIG; + + /* Deal with trailing bytes. */ + if (usize > ksize) { + int ret = clear_user(dst + size, rest); + if (ret) + return ret; + } + if (ignored_trailing) + *ignored_trailing = ksize < usize && + memchr_inv(src + size, 0, rest) != NULL; + /* Copy the interoperable parts of the struct. */ + if (copy_to_user(dst, src, size)) + return -EFAULT; + return 0; +} + bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size); long copy_from_kernel_nofault(void *dst, const void *src, size_t size); From patchwork Thu Sep 5 14:56:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aleksa Sarai X-Patchwork-Id: 13792505 Received: from mout-p-202.mailbox.org (mout-p-202.mailbox.org [80.241.56.172]) (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 0842919ADB9; Thu, 5 Sep 2024 14:57:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548256; cv=none; b=PUG/Ad0dsuGmRLhJXrzqu4OY3s0S9ZYcUYZAwAqnwzVD5gTJvY7o9VvNDrp5RfDqdhi6OdwvV8x02I7/YTORUrU3DBLHG/4oJjme7+57cIwF58qJbHShWAa7LWFz+YxFUUrARDjdAiFivvHacIGp5RHAkwmy/vz/+ouVuQBPESk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548256; c=relaxed/simple; bh=CJyjA4gBjrQqcN7SKwV2ogAyG7zP2fvwlMbtl9Q/2/0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=aNJvHYaDAOYtbueFC5ZMACQ6P6gwJrcaqXAqJQqAVpQaHonBhlXY4E/uJ+IBbiSGWiFOSU4Jak3pBFd64LvOPE0UoxIhDwbDi6MRglk5AQxc3tEf/c23mggqRPXvmYE8FEBFp63CLw0LQAB2pKcWf9KJ5aZgCsmPcTQ9TcLdhD4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com; spf=pass smtp.mailfrom=cyphar.com; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b=0vk45VJc; arc=none smtp.client-ip=80.241.56.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cyphar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b="0vk45VJc" Received: from smtp102.mailbox.org (smtp102.mailbox.org [10.196.197.102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-202.mailbox.org (Postfix) with ESMTPS id 4X02X46Mklz9sWG; Thu, 5 Sep 2024 16:57:24 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cyphar.com; s=MBO0001; t=1725548244; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=T7VuFq93k4shWsKttYPQiSWTE6fbnrqrqnw6DhIiaGM=; b=0vk45VJcttKQDOXnR+jtUHP1va4ng/Mx8pNZxmN7/DdaKfLRd/T8fAg+w4IAExm2FyKxsL sWv34gYTh0D7wRByCNE6z2GQ/iYspGr6dZBvkLaA+LJizYK4FzaiKQmM6k3QVFxQjWAijW 5yZgDzDO4QvGdh5QAIbT6MiOh6P7mG8lcLJ2Zob+gXamJmptl5o5RDmwytL9MtMB5O9FLe XEG9u+JUO3zrTwXFqresk1ECcmENY+CMOsLvIqndRwGBAOkzx4cfePOmT/85cSM4SK7lxg heqi582JEv+zAuCO17g0sZte3wFDUu/AaFuGP1PiIh/RHpnN37Ne/4j+DRUORg== From: Aleksa Sarai Date: Fri, 06 Sep 2024 00:56:34 +1000 Subject: [PATCH RFC v2 02/10] sched_getattr: port to copy_struct_to_user Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240906-extensible-structs-check_fields-v2-2-0f46d2de9bad@cyphar.com> References: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> In-Reply-To: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> To: Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Alexander Viro , Christian Brauner , Jan Kara , Arnd Bergmann , Shuah Khan Cc: Kees Cook , Florian Weimer , Arnd Bergmann , Mark Rutland , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, Aleksa Sarai X-Developer-Signature: v=1; a=openpgp-sha256; l=2389; i=cyphar@cyphar.com; h=from:subject:message-id; bh=CJyjA4gBjrQqcN7SKwV2ogAyG7zP2fvwlMbtl9Q/2/0=; b=owGbwMvMwCWmMf3Xpe0vXfIZT6slMaTdPLb5XcT7W1zejTlBn65oy2/+vn/GZ6nKZrcSp7RI3 oOS9yzlOkpZGMS4GGTFFFm2+XmGbpq/+Eryp5VsMHNYmUCGMHBxCsBEUpUY/srt0rmwa++uJTO/ VfAei93D+nRNfPbu12Xv3lwQ28C9Wms9I0NnfdDDpzVWaqt2JoT0WUetrzSNZvnnqq5z5IKkWuv q73wA X-Developer-Key: i=cyphar@cyphar.com; a=openpgp; fpr=C9C370B246B09F6DBCFC744C34401015D1D2D386 sched_getattr(2) doesn't care about trailing non-zero bytes in the (ksize > usize) case, so just use copy_struct_to_user() without checking ignored_trailing. Signed-off-by: Aleksa Sarai --- kernel/sched/syscalls.c | 42 ++---------------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c index ae1b42775ef9..4ccc058bae16 100644 --- a/kernel/sched/syscalls.c +++ b/kernel/sched/syscalls.c @@ -1147,45 +1147,6 @@ SYSCALL_DEFINE2(sched_getparam, pid_t, pid, struct sched_param __user *, param) return copy_to_user(param, &lp, sizeof(*param)) ? -EFAULT : 0; } -/* - * Copy the kernel size attribute structure (which might be larger - * than what user-space knows about) to user-space. - * - * Note that all cases are valid: user-space buffer can be larger or - * smaller than the kernel-space buffer. The usual case is that both - * have the same size. - */ -static int -sched_attr_copy_to_user(struct sched_attr __user *uattr, - struct sched_attr *kattr, - unsigned int usize) -{ - unsigned int ksize = sizeof(*kattr); - - if (!access_ok(uattr, usize)) - return -EFAULT; - - /* - * sched_getattr() ABI forwards and backwards compatibility: - * - * If usize == ksize then we just copy everything to user-space and all is good. - * - * If usize < ksize then we only copy as much as user-space has space for, - * this keeps ABI compatibility as well. We skip the rest. - * - * If usize > ksize then user-space is using a newer version of the ABI, - * which part the kernel doesn't know about. Just ignore it - tooling can - * detect the kernel's knowledge of attributes from the attr->size value - * which is set to ksize in this case. - */ - kattr->size = min(usize, ksize); - - if (copy_to_user(uattr, kattr, kattr->size)) - return -EFAULT; - - return 0; -} - /** * sys_sched_getattr - similar to sched_getparam, but with sched_attr * @pid: the pid in question. @@ -1230,7 +1191,8 @@ SYSCALL_DEFINE4(sched_getattr, pid_t, pid, struct sched_attr __user *, uattr, #endif } - return sched_attr_copy_to_user(uattr, &kattr, usize); + kattr.size = min(usize, sizeof(kattr)); + return copy_struct_to_user(uattr, usize, &kattr, sizeof(kattr), NULL); } #ifdef CONFIG_SMP From patchwork Thu Sep 5 14:56:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aleksa Sarai X-Patchwork-Id: 13792506 Received: from mout-p-101.mailbox.org (mout-p-101.mailbox.org [80.241.56.151]) (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 3C23819FA9F; Thu, 5 Sep 2024 14:57:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.151 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548259; cv=none; b=lbfhIb3iFLCj3ymXjBTMavgyWszW9FhFjstGIRLTNV9VNrMRdvZ81JulcybIp/KANGmlw3o6tnuxF5R3m/u3//YzNgF3NDMOf8Ml2id8qrN/SvvXpxKxPuKn5f+MZBIoM8M0dw/OMsiB3p4o6fVtNDH/5khmY38waYHWSrvEx8s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548259; c=relaxed/simple; bh=c4dE2X3D8jNW7VIHknDX4GKLOebCJIX98K1SweWgq9g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PT70VYE4QltR/VtpP6CSQzJokjYSHbEm3gZ5tUZasj8og4YE414R8uD0cgDhWCCZ5ja6UC7GPq+KnyoydLXuwrxJteE0bKS0QeLjhkRG/54mvMXguQpMjTYpWzkNR+SEVK2BiLW/RX1A5S/dbildSO2h+21BXgmdfYvulgJ3efc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com; spf=pass smtp.mailfrom=cyphar.com; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b=1APLUSii; arc=none smtp.client-ip=80.241.56.151 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cyphar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b="1APLUSii" Received: from smtp102.mailbox.org (smtp102.mailbox.org [IPv6:2001:67c:2050:b231:465::102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-101.mailbox.org (Postfix) with ESMTPS id 4X02XG3Mk0z9sFb; Thu, 5 Sep 2024 16:57:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cyphar.com; s=MBO0001; t=1725548254; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=YeczAUMJPFpgcChSC2NOpYh4duarO6U/B6665/OuVEY=; b=1APLUSiiNnoQ4talzWKkqnujLD2aR3XzevHzztypI+OPLTuF0cwE8pAsyi2lDnhIRrUTbd mo+yDzMwyCzX5T7EccrmDVs0o9QIr1mYRuzL8A7ShQLU+g6v2F+V7wI33oKYlMpnoUxJyG 9V0b1Xd96NGpeCBxI8qNF4uPO6wLAB6WjXRP0QIY7KglbLQHc5jMkkNQOGb6csRLgo4XRc YkY3PahRRodtLKgGSfKoW6d56jkqft9zJ9pi+5Q6yx/AoVSmOLONkVbeVHH4K4poAU592n 6AKHxSkBcsiLyiuSeybuXYqncGaXCV+6JaAv/wAY2iaPEB1TGSC3aIeLtPi4hw== From: Aleksa Sarai Date: Fri, 06 Sep 2024 00:56:35 +1000 Subject: [PATCH RFC v2 03/10] openat2: explicitly return -E2BIG for (usize > PAGE_SIZE) Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240906-extensible-structs-check_fields-v2-3-0f46d2de9bad@cyphar.com> References: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> In-Reply-To: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> To: Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Alexander Viro , Christian Brauner , Jan Kara , Arnd Bergmann , Shuah Khan Cc: Kees Cook , Florian Weimer , Arnd Bergmann , Mark Rutland , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, Aleksa Sarai , stable@vger.kernel.org X-Developer-Signature: v=1; a=openpgp-sha256; l=891; i=cyphar@cyphar.com; h=from:subject:message-id; bh=c4dE2X3D8jNW7VIHknDX4GKLOebCJIX98K1SweWgq9g=; b=owGbwMvMwCWmMf3Xpe0vXfIZT6slMaTdPLb5Uuwxl4NWleL9dttyrX9V5eVd5W+es+HB79mS5 nOKbRhXd5SyMIhxMciKKbJs8/MM3TR/8ZXkTyvZYOawMoEMYeDiFICJ1Hsw/OG53fk+QyqnT7Tu mPm/idOOum2fcPPBconnNv4pTOrlS1MYGT7zst2W+fxVZMaRry9y5Xcs7r9X8VNRs2SSy87GtHU cyewA X-Developer-Key: i=cyphar@cyphar.com; a=openpgp; fpr=C9C370B246B09F6DBCFC744C34401015D1D2D386 X-Rspamd-Queue-Id: 4X02XG3Mk0z9sFb While we do currently return -EFAULT in this case, it seems prudent to follow the behaviour of other syscalls like clone3. It seems quite unlikely that anyone depends on this error code being EFAULT, but we can always revert this if it turns out to be an issue. Cc: # v5.6+ Fixes: fddb5d430ad9 ("open: introduce openat2(2) syscall") Signed-off-by: Aleksa Sarai --- fs/open.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/open.c b/fs/open.c index 22adbef7ecc2..30bfcddd505d 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1458,6 +1458,8 @@ SYSCALL_DEFINE4(openat2, int, dfd, const char __user *, filename, if (unlikely(usize < OPEN_HOW_SIZE_VER0)) return -EINVAL; + if (unlikely(usize > PAGE_SIZE)) + return -E2BIG; err = copy_struct_from_user(&tmp, sizeof(tmp), how, usize); if (err) From patchwork Thu Sep 5 14:56:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aleksa Sarai X-Patchwork-Id: 13792507 Received: from mout-p-101.mailbox.org (mout-p-101.mailbox.org [80.241.56.151]) (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 B03CD1990DB; Thu, 5 Sep 2024 14:57:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.151 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548269; cv=none; b=K1yhso4Br4Szk4y3bn5OIOLONpiySiQyUmFHuqDEZLL+0QKMs3NHg2/X7z3YnSzYHx6F7V8OfIO7t7kRk74uY4iB5s0se/6z9Ao0/HYeJ4LCqpncQ/uDPu0Y5abmvpehSQCqCv+KbJ3713kQCB8uGzi3sU0hUCju/JtgBK+REsA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548269; c=relaxed/simple; bh=Q9mL5w2RoQs4wSB8ZJ2PbsaEFoNFfd5BJzx5Jald3aw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jmtNUod59imhIq0tMNECx2sxFye+GIxC8kRxlKucsmGtxo14J9VBGi1/TyKBEbX21OPbB566GvLoQbVCFqy0WI52VxxcfRWEh26OE9H4pbv9Zr8FBAYk4gTUZ2NfYDgSK9fF6Ygn+ZlYEXltFoZMKEj+l3iJXugwkTGTzX/bJSI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com; spf=pass smtp.mailfrom=cyphar.com; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b=iXFhQmWS; arc=none smtp.client-ip=80.241.56.151 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cyphar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b="iXFhQmWS" Received: from smtp102.mailbox.org (smtp102.mailbox.org [10.196.197.102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-101.mailbox.org (Postfix) with ESMTPS id 4X02XR5KzSz9ssx; Thu, 5 Sep 2024 16:57:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cyphar.com; s=MBO0001; t=1725548263; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=rBcPsrntGA2sR7NYUTm4f/0fqVqw1hf/h/mg1miiCzQ=; b=iXFhQmWSJhhLCBAtaSHppLoL1jtjPXzb76oO4iYwEDRZgp1RMVUCPPIqzJB7diXBw+zS+g FWLNIPLm9il95GBQSBFd8F48Ea/rL2SE+mweTnctFik1849Z0ZqmfDD6X7k7HN7Bp1nnfs 4tXt6qY/YUSRr43QcvO77c3nbBlk5k9Tu6NuaTRyY3D92pqtZbYMS7U4IUYMRW3dNlI8SG dWemq4pKNcLkYAnJhA7GZ+yJC+g2DJ8ViVB+dv3byDCH7ZeOa6HDmU1wy92vuSRqv18lSK i10GepIN/c/H0y4glaA84+q+V2v4yA0e35A4QbixtGoOuC3BrlGaQehgL8PlSw== From: Aleksa Sarai Date: Fri, 06 Sep 2024 00:56:36 +1000 Subject: [PATCH RFC v2 04/10] openat2: add CHECK_FIELDS flag to usize argument Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240906-extensible-structs-check_fields-v2-4-0f46d2de9bad@cyphar.com> References: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> In-Reply-To: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> To: Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Alexander Viro , Christian Brauner , Jan Kara , Arnd Bergmann , Shuah Khan Cc: Kees Cook , Florian Weimer , Arnd Bergmann , Mark Rutland , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, Aleksa Sarai X-Developer-Signature: v=1; a=openpgp-sha256; l=9718; i=cyphar@cyphar.com; h=from:subject:message-id; bh=Q9mL5w2RoQs4wSB8ZJ2PbsaEFoNFfd5BJzx5Jald3aw=; b=owGbwMvMwCWmMf3Xpe0vXfIZT6slMaTdPLaZl2N798T7HXO4d7QXt8T2Js+I9NR7//3jWpsFt 7epc1X1dJSyMIhxMciKKbJs8/MM3TR/8ZXkTyvZYOawMoEMYeDiFICJ6LYz/JU//px7N4/m7Tur BX+tMZIy3SXurnU86uKVlLPTrjJwnT3HyDCT2W698BTb3Gq/xu2fWo5neLC/ufJuT2aY7J4te12 cbnAAAA== X-Developer-Key: i=cyphar@cyphar.com; a=openpgp; fpr=C9C370B246B09F6DBCFC744C34401015D1D2D386 In order for userspace to be able to know what flags and fields the kernel supports, it is currently necessary for them to do a bunch of fairly subtle self-checks where you need to get a syscall to return a non-EINVAL error or no-op for each flag you wish to use. If you get -EINVAL you know the flag is unsupported, otherwise you know it is supported. This doesn't scale well for programs that need to check many flags, and not all syscalls can be easily checked (how would you check for new flags for umount2 or clone3 without side-effects?). To solve this problem, we can take advantage of the extensible struct API used by copy_struct_from_user() by providing a special CHECK_FIELDS flag to extensible struct syscalls (like openat2 and clone3) which will: 1. Cause the syscall to fill the structure with every valid bit the kernel understands. For flag arguments, this is the set of all valid flag bits. For pointer and file descriptor arguments, this would be all 0xFF bits (to indicate that any bits are valid). Userspace can then easily check whether the flag they wanted is supported (by doing a simple bitwise AND) or if a field itself is supported (by checking if it is non-zero / all 0xFF). 2. Return a specific no-op error (-EEXTSYS_NOOP) that is not used as an error by any other kernel code, so that userspace can be absolutely sure that the kernel supports CHECK_FIELDS. Rather than passing CHECK_FIELDS using the standard flags arguments for the syscall, CHECK_FIELDS is instead the highest bit in the provided struct size. The high bits of the size are never going to be non-zero (we currently only allow size to be up to PAGE_SIZE, and it seems very unlikely we will ever allow several exabyte structure arguments). By passing the flag in the structure size, we can be sure that old kernels will return a consistent error code (-EFAULT in openat2's case) and that seccomp can properly filter this syscall mode (which is guaranteed to be a no-op on all kernels -- it could even force -EEXTSYS_NOOP to make the userspace program think the kernel doesn't support any syscall features). The intended way of using this interface to get feature information looks something like the following (imagine that openat2 has gained a new field and a new flag in the future): static bool openat2_no_automount_supported; static bool openat2_cwd_fd_supported; int check_openat2_support(void) { int err; struct open_how how = {}; err = openat2(AT_FDCWD, ".", &how, CHECK_FIELDS | sizeof(how)); assert(err < 0); switch (errno) { case EFAULT: case E2BIG: /* Old kernel... */ check_support_the_old_way(); break; case EEXTSYS_NOOP: openat2_no_automount_supported = (how.flags & RESOLVE_NO_AUTOMOUNT); openat2_cwd_fd_supported = (how.cwd_fd != 0); break; } } Link: https://youtu.be/ggD-eb3yPVs Link: https://lwn.net/Articles/830666/ Signed-off-by: Aleksa Sarai --- arch/alpha/include/uapi/asm/errno.h | 3 +++ arch/mips/include/uapi/asm/errno.h | 3 +++ arch/parisc/include/uapi/asm/errno.h | 3 +++ arch/sparc/include/uapi/asm/errno.h | 3 +++ fs/open.c | 16 ++++++++++++++++ include/uapi/asm-generic/errno.h | 3 +++ include/uapi/linux/openat2.h | 2 ++ tools/arch/alpha/include/uapi/asm/errno.h | 3 +++ tools/arch/mips/include/uapi/asm/errno.h | 3 +++ tools/arch/parisc/include/uapi/asm/errno.h | 3 +++ tools/arch/sparc/include/uapi/asm/errno.h | 3 +++ tools/include/uapi/asm-generic/errno.h | 3 +++ 12 files changed, 48 insertions(+) diff --git a/arch/alpha/include/uapi/asm/errno.h b/arch/alpha/include/uapi/asm/errno.h index 3d265f6babaf..41157139fdad 100644 --- a/arch/alpha/include/uapi/asm/errno.h +++ b/arch/alpha/include/uapi/asm/errno.h @@ -125,4 +125,7 @@ #define EHWPOISON 139 /* Memory page has hardware error */ +/* For extensible syscalls. */ +#define EEXTSYS_NOOP 140 /* Extensible syscall performed no operation */ + #endif diff --git a/arch/mips/include/uapi/asm/errno.h b/arch/mips/include/uapi/asm/errno.h index 2fb714e2d6d8..dd1e0ba61105 100644 --- a/arch/mips/include/uapi/asm/errno.h +++ b/arch/mips/include/uapi/asm/errno.h @@ -124,6 +124,9 @@ #define EHWPOISON 168 /* Memory page has hardware error */ +/* For extensible syscalls. */ +#define EEXTSYS_NOOP 169 /* Extensible syscall performed no operation */ + #define EDQUOT 1133 /* Quota exceeded */ diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h index 8d94739d75c6..e2f6998ad61c 100644 --- a/arch/parisc/include/uapi/asm/errno.h +++ b/arch/parisc/include/uapi/asm/errno.h @@ -122,4 +122,7 @@ #define EHWPOISON 257 /* Memory page has hardware error */ +/* For extensible syscalls. */ +#define EEXTSYS_NOOP 258 /* Extensible syscall performed no operation */ + #endif diff --git a/arch/sparc/include/uapi/asm/errno.h b/arch/sparc/include/uapi/asm/errno.h index 81a732b902ee..1b11e4651093 100644 --- a/arch/sparc/include/uapi/asm/errno.h +++ b/arch/sparc/include/uapi/asm/errno.h @@ -115,4 +115,7 @@ #define EHWPOISON 135 /* Memory page has hardware error */ +/* For extensible syscalls. */ +#define EEXTSYS_NOOP 136 /* Extensible syscall performed no operation */ + #endif diff --git a/fs/open.c b/fs/open.c index 30bfcddd505d..3fbb0c82fece 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1451,16 +1451,32 @@ SYSCALL_DEFINE4(openat2, int, dfd, const char __user *, filename, struct open_how __user *, how, size_t, usize) { int err; + bool check_fields; struct open_how tmp; BUILD_BUG_ON(sizeof(struct open_how) < OPEN_HOW_SIZE_VER0); BUILD_BUG_ON(sizeof(struct open_how) != OPEN_HOW_SIZE_LATEST); + check_fields = usize & CHECK_FIELDS; + usize &= ~CHECK_FIELDS; + if (unlikely(usize < OPEN_HOW_SIZE_VER0)) return -EINVAL; if (unlikely(usize > PAGE_SIZE)) return -E2BIG; + if (unlikely(check_fields)) { + memset(&tmp, 0, sizeof(tmp)); + tmp = (struct open_how) { + .flags = VALID_OPEN_FLAGS, + .mode = S_IALLUGO, + .resolve = VALID_RESOLVE_FLAGS, + }; + + err = copy_struct_to_user(how, usize, &tmp, sizeof(tmp), NULL); + return err ?: -EEXTSYS_NOOP; + } + err = copy_struct_from_user(&tmp, sizeof(tmp), how, usize); if (err) return err; diff --git a/include/uapi/asm-generic/errno.h b/include/uapi/asm-generic/errno.h index cf9c51ac49f9..f5bfe081e73a 100644 --- a/include/uapi/asm-generic/errno.h +++ b/include/uapi/asm-generic/errno.h @@ -120,4 +120,7 @@ #define EHWPOISON 133 /* Memory page has hardware error */ +/* For extensible syscalls. */ +#define EEXTSYS_NOOP 134 /* Extensible syscall performed no operation */ + #endif diff --git a/include/uapi/linux/openat2.h b/include/uapi/linux/openat2.h index a5feb7604948..6052a504cfa4 100644 --- a/include/uapi/linux/openat2.h +++ b/include/uapi/linux/openat2.h @@ -4,6 +4,8 @@ #include +#define CHECK_FIELDS (1ULL << 63) + /* * Arguments for how openat2(2) should open the target path. If only @flags and * @mode are non-zero, then openat2(2) operates very similarly to openat(2). diff --git a/tools/arch/alpha/include/uapi/asm/errno.h b/tools/arch/alpha/include/uapi/asm/errno.h index 3d265f6babaf..41157139fdad 100644 --- a/tools/arch/alpha/include/uapi/asm/errno.h +++ b/tools/arch/alpha/include/uapi/asm/errno.h @@ -125,4 +125,7 @@ #define EHWPOISON 139 /* Memory page has hardware error */ +/* For extensible syscalls. */ +#define EEXTSYS_NOOP 140 /* Extensible syscall performed no operation */ + #endif diff --git a/tools/arch/mips/include/uapi/asm/errno.h b/tools/arch/mips/include/uapi/asm/errno.h index 2fb714e2d6d8..dd1e0ba61105 100644 --- a/tools/arch/mips/include/uapi/asm/errno.h +++ b/tools/arch/mips/include/uapi/asm/errno.h @@ -124,6 +124,9 @@ #define EHWPOISON 168 /* Memory page has hardware error */ +/* For extensible syscalls. */ +#define EEXTSYS_NOOP 169 /* Extensible syscall performed no operation */ + #define EDQUOT 1133 /* Quota exceeded */ diff --git a/tools/arch/parisc/include/uapi/asm/errno.h b/tools/arch/parisc/include/uapi/asm/errno.h index 8d94739d75c6..e2f6998ad61c 100644 --- a/tools/arch/parisc/include/uapi/asm/errno.h +++ b/tools/arch/parisc/include/uapi/asm/errno.h @@ -122,4 +122,7 @@ #define EHWPOISON 257 /* Memory page has hardware error */ +/* For extensible syscalls. */ +#define EEXTSYS_NOOP 258 /* Extensible syscall performed no operation */ + #endif diff --git a/tools/arch/sparc/include/uapi/asm/errno.h b/tools/arch/sparc/include/uapi/asm/errno.h index 81a732b902ee..1b11e4651093 100644 --- a/tools/arch/sparc/include/uapi/asm/errno.h +++ b/tools/arch/sparc/include/uapi/asm/errno.h @@ -115,4 +115,7 @@ #define EHWPOISON 135 /* Memory page has hardware error */ +/* For extensible syscalls. */ +#define EEXTSYS_NOOP 136 /* Extensible syscall performed no operation */ + #endif diff --git a/tools/include/uapi/asm-generic/errno.h b/tools/include/uapi/asm-generic/errno.h index cf9c51ac49f9..f5bfe081e73a 100644 --- a/tools/include/uapi/asm-generic/errno.h +++ b/tools/include/uapi/asm-generic/errno.h @@ -120,4 +120,7 @@ #define EHWPOISON 133 /* Memory page has hardware error */ +/* For extensible syscalls. */ +#define EEXTSYS_NOOP 134 /* Extensible syscall performed no operation */ + #endif From patchwork Thu Sep 5 14:56:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aleksa Sarai X-Patchwork-Id: 13792508 Received: from mout-p-202.mailbox.org (mout-p-202.mailbox.org [80.241.56.172]) (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 7B73B7F7FC; Thu, 5 Sep 2024 14:57:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548278; cv=none; b=BIaJGzUHPvNX/rWMLtmIxx0uUqVi8Ua9jDisBLfCof/HhPtMsLPLHZEeuoWgCraZldMTmWLNvUiMICSpgLefgh74ICAQ8Eo5ORmxUHZg49vrL4cMKMqyL/iXGctXoJnGYbBV7o9risllzUrmvol7ExOx40P7YkxkECBd7Fk4f8A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548278; c=relaxed/simple; bh=Xo4dbP+N+dKe3SIozVX57QvOYTkX/g8hqX8+Psww87E=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Y4pqfw8FBr18MRLFEaV5FT2+Bo8pS91CJ7Y4VLpLqQZqeODprGrgy75v9s86Z2AYnGMuD7xsldzkJ89Ib7AtU+4D50DG2x6bb0UufMGYb0q6wuD00/hTnDsCydQpKW8KP/yBCOgrzz6zTtEpwhSCfmYUE094SM0Ds+LfoC41l30= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com; spf=pass smtp.mailfrom=cyphar.com; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b=R2cto8b3; arc=none smtp.client-ip=80.241.56.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cyphar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b="R2cto8b3" Received: from smtp102.mailbox.org (smtp102.mailbox.org [10.196.197.102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-202.mailbox.org (Postfix) with ESMTPS id 4X02Xc6nzkz9t6V; Thu, 5 Sep 2024 16:57:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cyphar.com; s=MBO0001; t=1725548273; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=j6zDx/4wSgap3qpn522sEqjp0S1pufmopLG4YO9X8BM=; b=R2cto8b3gJsoczRAl9qHVBmzwA+YGlQNupg9uzRnF0t+IJpqzbN2k11zidq/Wa/TT2BLyT SQZR9M3zqz4w5TLYrkNbxw1v6qPprWmYunKu0WpmUMoc16tsazztDoyyyV0WGd+YZHgD9X plqdUOI4V25DLw7GJgz3tjAu0mV9lh5E6M3daGLp4T/p+v8W0WIC3uIoFT8y+EIFbaUEG2 g2DTCOK8V9zeH3zWHib1l4XoAuU7D33Zoff0peCtTPfI3xr+nKUibwlGR/PwV+BgZStoOK Wsol534A2RV5sIcZWaM+fW/1eD3JBhywmyeiBPbba9htIBTcswgxsgricxsUCA== From: Aleksa Sarai Date: Fri, 06 Sep 2024 00:56:37 +1000 Subject: [PATCH RFC v2 05/10] selftests: openat2: add 0xFF poisoned data after misaligned struct Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240906-extensible-structs-check_fields-v2-5-0f46d2de9bad@cyphar.com> References: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> In-Reply-To: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> To: Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Alexander Viro , Christian Brauner , Jan Kara , Arnd Bergmann , Shuah Khan Cc: Kees Cook , Florian Weimer , Arnd Bergmann , Mark Rutland , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, Aleksa Sarai X-Developer-Signature: v=1; a=openpgp-sha256; l=1132; i=cyphar@cyphar.com; h=from:subject:message-id; bh=Xo4dbP+N+dKe3SIozVX57QvOYTkX/g8hqX8+Psww87E=; b=owGbwMvMwCWmMf3Xpe0vXfIZT6slMaTdPLb5URWz6trbTC5piQHcm77/FZz3/enFDtHpR2zTP GbK2Ss/6ihlYRDjYpAVU2TZ5ucZumn+4ivJn1aywcxhZQIZwsDFKQATWe/B8D/ieLpMZPYCe9nM Xx/MnyyzNAvQY7/68rZtsXRUlwHTIQFGhv1NJbfOVM48qVUQ8VLbikPJ859avujMEzd2RromfGO 0ZgcA X-Developer-Key: i=cyphar@cyphar.com; a=openpgp; fpr=C9C370B246B09F6DBCFC744C34401015D1D2D386 We should also verify that poisoned data after a misaligned struct is also handled correctly by is_zeroed_user(). This test passes with no kernel changes needed, so is_zeroed_user() was correct already. Fixes: b28a10aedcd4 ("selftests: add openat2(2) selftests") Signed-off-by: Aleksa Sarai --- tools/testing/selftests/openat2/openat2_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/openat2/openat2_test.c b/tools/testing/selftests/openat2/openat2_test.c index 5790ab446527..4ca175a16ad6 100644 --- a/tools/testing/selftests/openat2/openat2_test.c +++ b/tools/testing/selftests/openat2/openat2_test.c @@ -112,9 +112,9 @@ void test_openat2_struct(void) * * This is effectively to check that is_zeroed_user() works. */ - copy = malloc(misalign + sizeof(how_ext)); + copy = malloc(misalign*2 + sizeof(how_ext)); how_copy = copy + misalign; - memset(copy, 0xff, misalign); + memset(copy, 0xff, misalign*2 + sizeof(how_ext)); memcpy(how_copy, &how_ext, sizeof(how_ext)); } From patchwork Thu Sep 5 14:56:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aleksa Sarai X-Patchwork-Id: 13792509 Received: from mout-p-103.mailbox.org (mout-p-103.mailbox.org [80.241.56.161]) (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 CD74D19D081; Thu, 5 Sep 2024 14:58:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.161 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548287; cv=none; b=haddlrcmk9GLZu6RUvpvAuqcshcsAt5PKjq6gE5HEOZXCwwRJdl1UGxDH3SzxvWz2sslNzOMDyXik6EkkmVGbghY77x5hgnDRfIeEU7+Sae+wI2dkh7Nq0X41PNs7bo1IeGisTnfhKrmFBXB7XwHTMW5iXlN3Q0Cf51XutvBYIM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548287; c=relaxed/simple; bh=4XzvkTIhhT/HmkS5lBvB05Y6vkKNly+AlJMAP6Fz2PM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=nUctvieBCrPIBFRp/iZtsq5cBqOYobvYbd2jZeGYB76sK6E+3nyHdKWLJDhhmzmOapbjqjYrRcmPRePkz/cVCihB+0CEGryP6y1GjFe15LZxU5DNGfE3t5+fMZ/Dwi80AzYpXce3tuc/2LES7E0oNlPLroNNOdgYTL4yi5D9AO4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com; spf=pass smtp.mailfrom=cyphar.com; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b=L+9O7ijg; arc=none smtp.client-ip=80.241.56.161 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cyphar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b="L+9O7ijg" Received: from smtp102.mailbox.org (smtp102.mailbox.org [IPv6:2001:67c:2050:b231:465::102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-103.mailbox.org (Postfix) with ESMTPS id 4X02Xp3SHzz9shb; Thu, 5 Sep 2024 16:58:02 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cyphar.com; s=MBO0001; t=1725548282; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ylY/xAKp0iag5FTcnJtAz27611evPoJzrroecSSdN4s=; b=L+9O7ijgI9hYdcMETysrohxKLIrnroE4u9IfYJogLeM30p+/z3uKXyPG92LWXajV4jS8P6 v7fgiyKYu91gV42jzByrSgXigch9KV/pWdLJcI55MKlhl84j7AyZ9TePbKFDYohrx+gwh7 RaWEFUE5wgyQMrru5Rlb1GNRj4kSCxVYEIH3fsO31RdPLO+p8n8GKoYpSB0L6A0WgSQ5ra zITRZ9gyP6r0oOJj4gUnZEVCmW3OfXxrZeTZ79W+pyATf1kwLpoXF1c3YbTPV2J34b1bmy KZuOD2JWwIz9wqUKwJrvkDMmxqfkVoNmUHoQY8hw2IWrFeJULyiNvev0r909Xw== From: Aleksa Sarai Date: Fri, 06 Sep 2024 00:56:38 +1000 Subject: [PATCH RFC v2 06/10] selftests: openat2: add CHECK_FIELDS selftests Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240906-extensible-structs-check_fields-v2-6-0f46d2de9bad@cyphar.com> References: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> In-Reply-To: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> To: Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Alexander Viro , Christian Brauner , Jan Kara , Arnd Bergmann , Shuah Khan Cc: Kees Cook , Florian Weimer , Arnd Bergmann , Mark Rutland , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, Aleksa Sarai X-Developer-Signature: v=1; a=openpgp-sha256; l=7278; i=cyphar@cyphar.com; h=from:subject:message-id; bh=4XzvkTIhhT/HmkS5lBvB05Y6vkKNly+AlJMAP6Fz2PM=; b=owGbwMvMwCWmMf3Xpe0vXfIZT6slMaTdPLalojD5eNv9F9xp8dsT/L81R28xiBddKXigrn2+/ 8cmBdFZHaUsDGJcDLJiiizb/DxDN81ffCX500o2mDmsTCBDGLg4BWAiHAqMDBPuLHBWTwx8wyLu s7DknMXMI/Pfe+dt1Zqgq/u6IO7PyXsM/532l/IbpN3mcLExelp95mfE19WXPCa48G3n/JHCtWF PGjsA X-Developer-Key: i=cyphar@cyphar.com; a=openpgp; fpr=C9C370B246B09F6DBCFC744C34401015D1D2D386 X-Rspamd-Queue-Id: 4X02Xp3SHzz9shb Signed-off-by: Aleksa Sarai --- tools/testing/selftests/openat2/Makefile | 2 + tools/testing/selftests/openat2/openat2_test.c | 161 ++++++++++++++++++++++++- 2 files changed, 161 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/openat2/Makefile b/tools/testing/selftests/openat2/Makefile index 185dc76ebb5f..239a7fd0cb35 100644 --- a/tools/testing/selftests/openat2/Makefile +++ b/tools/testing/selftests/openat2/Makefile @@ -12,6 +12,8 @@ ifeq ($(LLVM),) endif LOCAL_HDRS += helpers.h +# Needed for EEXTSYS_NOOP definition. +CFLAGS += $(TOOLS_INCLUDES) include ../lib.mk diff --git a/tools/testing/selftests/openat2/openat2_test.c b/tools/testing/selftests/openat2/openat2_test.c index 4ca175a16ad6..9d3382bdfce9 100644 --- a/tools/testing/selftests/openat2/openat2_test.c +++ b/tools/testing/selftests/openat2/openat2_test.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Author: Aleksa Sarai - * Copyright (C) 2018-2019 SUSE LLC. + * Copyright (C) 2018-2024 SUSE LLC. */ #define _GNU_SOURCE #define __SANE_USERSPACE_TYPES__ // Use ll64 +#include #include #include #include @@ -29,6 +30,10 @@ #define O_LARGEFILE 0x8000 #endif +#ifndef CHECK_FIELDS +#define CHECK_FIELDS (1ULL << 63) +#endif + struct open_how_ext { struct open_how inner; uint32_t extra1; @@ -45,6 +50,152 @@ struct struct_test { int err; }; +static bool check(bool *failed, bool pred) +{ + *failed |= pred; + return pred; +} + +#define NUM_OPENAT2_CHECK_FIELDS_BAD_TESTS 2 + +static void test_openat2_check_fields_bad(const char *name, size_t struct_size) +{ + int fd, expected_err; + bool failed = false; + struct open_how how = {}; + void (*resultfn)(const char *msg, ...) = ksft_test_result_pass; + + if (struct_size < sizeof(struct open_how)) + expected_err = -EINVAL; + if (struct_size > 4096) + expected_err = -E2BIG; + + if (!openat2_supported) { + ksft_print_msg("openat2(2) unsupported\n"); + resultfn = ksft_test_result_skip; + goto skip; + } + + fd = raw_openat2(AT_FDCWD, "", &how, CHECK_FIELDS | struct_size); + if (check(&failed, fd != expected_err)) + ksft_print_msg("openat2(CHECK_FIELDS) returned wrong error code: %d (%s) != %d (%s)\n", + fd, strerror(-fd), + expected_err, strerror(-expected_err)); + + if (fd >= 0) + close(fd); + + if (failed) + resultfn = ksft_test_result_fail; + +skip: + resultfn("openat2(CHECK_FIELDS) with %s returns %d (%s)\n", name, + expected_err, strerror(-expected_err)); + fflush(stdout); +} + +#define NUM_OPENAT2_CHECK_FIELDS_TESTS 1 +#define NUM_OPENAT2_CHECK_FIELDS_VARIATIONS 13 + +static void test_openat2_check_fields(void) +{ + int misalignments[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 17, 87 }; + + for (int i = 0; i < ARRAY_LEN(misalignments); i++) { + int fd, misalign = misalignments[i]; + bool failed = false; + char *fdpath = NULL; + void (*resultfn)(const char *msg, ...) = ksft_test_result_pass; + + struct open_how_ext how_ext = {}, *how_copy = &how_ext; + void *copy = NULL; + + if (!openat2_supported) { + ksft_print_msg("openat2(2) unsupported\n"); + resultfn = ksft_test_result_skip; + goto skip; + } + + if (misalign) { + /* + * Explicitly misalign the structure copying it with + * the given (mis)alignment offset. The other data is + * set to zero and we verify this afterwards to make + * sure CHECK_FIELDS doesn't write outside the buffer. + */ + copy = malloc(misalign*2 + sizeof(how_ext)); + how_copy = copy + misalign; + memset(copy, 0x00, misalign*2 + sizeof(how_ext)); + memcpy(how_copy, &how_ext, sizeof(how_ext)); + } + + fd = raw_openat2(AT_FDCWD, ".", how_copy, CHECK_FIELDS | sizeof(*how_copy)); + if (check(&failed, (fd != -EEXTSYS_NOOP))) + ksft_print_msg("openat2(CHECK_FIELDS) returned wrong error code: %d (%s) != %d\n", + fd, strerror(-fd), -EEXTSYS_NOOP); + if (fd >= 0) { + fdpath = fdreadlink(fd); + close(fd); + } + + if (failed) { + ksft_print_msg("openat2(CHECK_FIELDS) unexpectedly returned "); + if (fdpath) + ksft_print_msg("%d['%s']\n", fd, fdpath); + else + ksft_print_msg("%d (%s)\n", fd, strerror(-fd)); + } + + if (check(&failed, !(how_copy->inner.flags & O_PATH))) + ksft_print_msg("openat2(CHECK_FIELDS) returned flags is missing O_PATH (0x%.16x): 0x%.16llx\n", + O_PATH, how_copy->inner.flags); + + if (check(&failed, (how_copy->inner.mode != 07777))) + ksft_print_msg("openat2(CHECK_FIELDS) returned mode is invalid (0o%o): 0o%.4llo\n", + 07777, how_copy->inner.mode); + + if (check(&failed, !(how_copy->inner.resolve & RESOLVE_IN_ROOT))) + ksft_print_msg("openat2(CHECK_FIELDS) returned resolve flags is missing RESOLVE_IN_ROOT (0x%.16x): 0x%.16llx\n", + RESOLVE_IN_ROOT, how_copy->inner.resolve); + + /* Verify that the buffer space outside the struct wasn't written to. */ + if (check(&failed, how_copy->extra1 != 0)) + ksft_print_msg("openat2(CHECK_FIELDS) touched a byte outside open_how (extra1): 0x%x\n", + how_copy->extra1); + if (check(&failed, how_copy->extra2 != 0)) + ksft_print_msg("openat2(CHECK_FIELDS) touched a byte outside open_how (extra2): 0x%x\n", + how_copy->extra2); + if (check(&failed, how_copy->extra3 != 0)) + ksft_print_msg("openat2(CHECK_FIELDS) touched a byte outside open_how (extra3): 0x%x\n", + how_copy->extra3); + + if (misalign) { + for (size_t i = 0; i < misalign; i++) { + char *p = copy + i; + if (check(&failed, *p != '\x00')) + ksft_print_msg("openat2(CHECK_FIELDS) touched a byte outside the size: buffer[%ld] = 0x%.2x\n", + p - (char *) copy, *p); + } + for (size_t i = 0; i < misalign; i++) { + char *p = copy + misalign + sizeof(how_ext) + i; + if (check(&failed, *p != '\x00')) + ksft_print_msg("openat2(CHECK_FIELDS) touched a byte outside the size: buffer[%ld] = 0x%.2x\n", + p - (char *) copy, *p); + } + } + + if (failed) + resultfn = ksft_test_result_fail; + +skip: + resultfn("openat2(CHECK_FIELDS) [misalign=%d]\n", misalign); + + free(copy); + free(fdpath); + fflush(stdout); + } +} + #define NUM_OPENAT2_STRUCT_TESTS 7 #define NUM_OPENAT2_STRUCT_VARIATIONS 13 @@ -320,7 +471,9 @@ void test_openat2_flags(void) } } -#define NUM_TESTS (NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \ +#define NUM_TESTS (NUM_OPENAT2_CHECK_FIELDS_TESTS * NUM_OPENAT2_CHECK_FIELDS_VARIATIONS + \ + NUM_OPENAT2_CHECK_FIELDS_BAD_TESTS + \ + NUM_OPENAT2_STRUCT_VARIATIONS * NUM_OPENAT2_STRUCT_TESTS + \ NUM_OPENAT2_FLAG_TESTS) int main(int argc, char **argv) @@ -328,6 +481,10 @@ int main(int argc, char **argv) ksft_print_header(); ksft_set_plan(NUM_TESTS); + test_openat2_check_fields(); + test_openat2_check_fields_bad("small struct (< v0)", 0); + test_openat2_check_fields_bad("large struct (> PAGE_SIZE)", 0xF000); + test_openat2_struct(); test_openat2_flags(); From patchwork Thu Sep 5 14:56:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aleksa Sarai X-Patchwork-Id: 13792510 Received: from mout-p-202.mailbox.org (mout-p-202.mailbox.org [80.241.56.172]) (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 6A37D6E614; Thu, 5 Sep 2024 14:58:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548297; cv=none; b=cU3EogKP5vWlBoeXAwRYHY8V1x/S5t5xQtiNMGg8zWx1eRZT2vPxaiYPUTUh0e6c/c+j795b/nQD2bfS7Ggezg7Rq3wS6yVH+2hRDA+72EGM29r13WjMvIPOJzSSRa/q/w1P8usB2BWAaTRzZvxpy1T7J22jybjcOrqHLlbrG0A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548297; c=relaxed/simple; bh=F4+nNIP45rPDTnusEiNx0LRIIo0bh6KraTxorV1cxo4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tiw0yssSSOFwjQEGqEYY3freItvxwiak2RcMtX+kUxiBpF94nbqqUZAOlddozjM17jcuGD0MM1Kz3Mz12wTYviw6qqV6HMvbeM8QZyTq9WXkDp+f27Mtywr3VMeSRyCRxRgPURpYTWPvJlNB7UBair/fzUfsMEZ7mx0uypkqwig= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com; spf=pass smtp.mailfrom=cyphar.com; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b=RnR9oQcP; arc=none smtp.client-ip=80.241.56.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cyphar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b="RnR9oQcP" Received: from smtp102.mailbox.org (smtp102.mailbox.org [IPv6:2001:67c:2050:b231:465::102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-202.mailbox.org (Postfix) with ESMTPS id 4X02Xz5Y52z9t6V; Thu, 5 Sep 2024 16:58:11 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cyphar.com; s=MBO0001; t=1725548291; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=v5CFQmS4dg9kR/Y1AQq6jUMvui07sBICi7CdGXa1jOM=; b=RnR9oQcP6flLcUpDRm66sWqD5s4cdBHutixL3k3ZFVJJjwIJY4UkEjLSW6klQzZb97CAdk +GgL+avzS43wpGM2DkvKYgBLFnWzo1o5Ip2Gxun1kf1+urTdUZD76FSbKiZL8Y2U1eXpnv TGHmdYfjYqXVIgpLoROab+oI3CF+cdQ/sy5AMECdQY8O2Yk5kFpHcYnelyWr9Nu9F4A5aH o/SNfCM0dV0kZVKyOrcRG9rVb0qnfhIoV3h2F9OHZitAyUqtwIXNUtaoHUcbsYFPfRyHPz 740ZWUhT7detwW39RYNbQsiSChduOc/LvxUkWLrZq2tcYFGlTLdYIQu2VE7kKg== From: Aleksa Sarai Date: Fri, 06 Sep 2024 00:56:39 +1000 Subject: [PATCH RFC v2 07/10] clone3: add CHECK_FIELDS flag to usize argument Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240906-extensible-structs-check_fields-v2-7-0f46d2de9bad@cyphar.com> References: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> In-Reply-To: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> To: Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Alexander Viro , Christian Brauner , Jan Kara , Arnd Bergmann , Shuah Khan Cc: Kees Cook , Florian Weimer , Arnd Bergmann , Mark Rutland , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, Aleksa Sarai X-Developer-Signature: v=1; a=openpgp-sha256; l=3623; i=cyphar@cyphar.com; h=from:subject:message-id; bh=F4+nNIP45rPDTnusEiNx0LRIIo0bh6KraTxorV1cxo4=; b=owGbwMvMwCWmMf3Xpe0vXfIZT6slMaTdPLYlqadKg7frn47bipXXp1mKn+YoWX8ukSFqYuuWb 1tmSO2q7ShlYRDjYpAVU2TZ5ucZumn+4ivJn1aywcxhZQIZwsDFKQAT+fuOkWHX5bep1yP9w4r/ Pgt8zLtoosvmvH9n/k5yqPW+yxn82GAzI8MBp+sHzZVelu9qi67i6wqsmnBzls5+i5t3jQ+rJi7 VSeUDAA== X-Developer-Key: i=cyphar@cyphar.com; a=openpgp; fpr=C9C370B246B09F6DBCFC744C34401015D1D2D386 X-Rspamd-Queue-Id: 4X02Xz5Y52z9t6V As with openat2(2), this allows userspace to easily figure out what flags and fields are supported by clone3(2). For fields which are not flag-based, we simply set every bit in the field so that a naive bitwise-and would show that any value of the field is valid. For args->exit_signal, since we have an explicit bitmask for the field defined already (CSIGNAL) we can indicate that only those bits are supported by current kernels. If we add some extra bits to exit_signal in the future, being able to detect them as new features would be quite useful. The intended way of using this interface to get feature information looks something like the following: static bool clone3_clear_sighand_supported; static bool clone3_cgroup_supported; int check_clone3_support(void) { int err; struct clone_args args = {}; err = clone3(&args, CHECK_FIELDS | sizeof(args)); assert(err < 0); switch (errno) { case EFAULT: case E2BIG: /* Old kernel... */ check_support_the_old_way(); break; case EEXTSYS_NOOP: clone3_clear_sighand_supported = (how.flags & CLONE_CLEAR_SIGHAND); clone3_cgroup_supported = (how.flags & CLONE_INTO_CGROUP) && (how.cgroup != 0); break; } } Signed-off-by: Aleksa Sarai --- kernel/fork.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/kernel/fork.c b/kernel/fork.c index cc760491f201..bded93f4f5f4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2925,11 +2925,15 @@ SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, } #endif + +#define CLONE3_VALID_FLAGS (CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP) + noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs, struct clone_args __user *uargs, size_t usize) { int err; + bool check_fields; struct clone_args args; pid_t *kset_tid = kargs->set_tid; @@ -2941,11 +2945,34 @@ noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs, CLONE_ARGS_SIZE_VER2); BUILD_BUG_ON(sizeof(struct clone_args) != CLONE_ARGS_SIZE_VER2); + check_fields = usize & CHECK_FIELDS; + usize &= ~CHECK_FIELDS; + if (unlikely(usize > PAGE_SIZE)) return -E2BIG; if (unlikely(usize < CLONE_ARGS_SIZE_VER0)) return -EINVAL; + if (unlikely(check_fields)) { + memset(&args, 0, sizeof(args)); + args = (struct clone_args) { + .flags = CLONE3_VALID_FLAGS, + .pidfd = 0xFFFFFFFFFFFFFFFF, + .child_tid = 0xFFFFFFFFFFFFFFFF, + .parent_tid = 0xFFFFFFFFFFFFFFFF, + .exit_signal = (u64) CSIGNAL, + .stack = 0xFFFFFFFFFFFFFFFF, + .stack_size = 0xFFFFFFFFFFFFFFFF, + .tls = 0xFFFFFFFFFFFFFFFF, + .set_tid = 0xFFFFFFFFFFFFFFFF, + .set_tid_size = 0xFFFFFFFFFFFFFFFF, + .cgroup = 0xFFFFFFFFFFFFFFFF, + }; + + err = copy_struct_to_user(uargs, usize, &args, sizeof(args), NULL); + return err ?: -EEXTSYS_NOOP; + } + err = copy_struct_from_user(&args, sizeof(args), uargs, usize); if (err) return err; @@ -3025,8 +3052,7 @@ static inline bool clone3_stack_valid(struct kernel_clone_args *kargs) static bool clone3_args_valid(struct kernel_clone_args *kargs) { /* Verify that no unknown flags are passed along. */ - if (kargs->flags & - ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP)) + if (kargs->flags & ~CLONE3_VALID_FLAGS) return false; /* From patchwork Thu Sep 5 14:56:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aleksa Sarai X-Patchwork-Id: 13792511 Received: from mout-p-201.mailbox.org (mout-p-201.mailbox.org [80.241.56.171]) (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 CD2896E614; Thu, 5 Sep 2024 14:58:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548307; cv=none; b=JOnt3ZPFVwfh0KJj4ZTEX5HyPjKAy+qxalhi8El1jEcccPttd0BE5OufMMFSrrUhC/VrjVCzSIoxwGJmrIePHg+HL7ZSWEMuCvs9qvEzCdJnsZAzqwzHOPkI0S4jn9Qo4pmEncRLG46bdNsIZNRWvVCgFG9HsojdOL0DgsVbv48= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548307; c=relaxed/simple; bh=kRrwzdOq+SA1pM6poEFMWl0XQ/sbCIhZAdJlYHIAUwo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rmqgP8BQTIE5Litc5KgXPzN/FjpJhKNSllNaJUnWOAenlGTldGGHZomRdanvEtC8zoOy7DFWy0rf9uYu25/LvwGufpUesYhFmgQk6b4viUEh4G57VGEZwrxg1GSZgVzOqLMSgZkCy6sItgvUgn6J5Pe8m5NRnOm2tsGCXEmUcq8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com; spf=pass smtp.mailfrom=cyphar.com; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b=1cEZcqIw; arc=none smtp.client-ip=80.241.56.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cyphar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b="1cEZcqIw" Received: from smtp102.mailbox.org (smtp102.mailbox.org [10.196.197.102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-201.mailbox.org (Postfix) with ESMTPS id 4X02Y90WXTz9t3T; Thu, 5 Sep 2024 16:58:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cyphar.com; s=MBO0001; t=1725548301; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RVmg8fAPgaDn1QmRe+7NfwAiQm9p8MO8EIkCEXBOUqA=; b=1cEZcqIwXS5HO7dVnENklwZ8Kst6LRQfjRP5dM2RM/8kMCL6XHre6YzBjeRIxgl86/Zm4X wK7g5fbVefKv0aY0h/JvizQSITPzAFayVslR1G8LPcSHV312Xe1ReCWBkif83JY9pk4ysG WCrPUbNkpXlGGB2PaK9QcaWUxglQtRZtRwypCjWGBrSxSXq0XpMo6F7lDKTJLltUt5G9t/ EL0zTvHoNUeSgMBOy+Sq44tYY/aIx1ZXdvGPq7JHyK/ogAnG+ervaCJAwfDb1GJqDv8rHE yyglnD0gQNbXnZXqkW7f4HbBp/lHU4PpaW6GVTyUTMQiwufWRtJgdt99Y/d+QA== From: Aleksa Sarai Date: Fri, 06 Sep 2024 00:56:40 +1000 Subject: [PATCH RFC v2 08/10] selftests: clone3: add CHECK_FIELDS selftests Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240906-extensible-structs-check_fields-v2-8-0f46d2de9bad@cyphar.com> References: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> In-Reply-To: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> To: Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Alexander Viro , Christian Brauner , Jan Kara , Arnd Bergmann , Shuah Khan Cc: Kees Cook , Florian Weimer , Arnd Bergmann , Mark Rutland , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, Aleksa Sarai X-Developer-Signature: v=1; a=openpgp-sha256; l=9631; i=cyphar@cyphar.com; h=from:subject:message-id; bh=kRrwzdOq+SA1pM6poEFMWl0XQ/sbCIhZAdJlYHIAUwo=; b=owGbwMvMwCWmMf3Xpe0vXfIZT6slMaTdPLbF/ILctH/r5led3cLKb7rkyxKDLo2rP9JSjnFo6 2zY/uf8iY5SFgYxLgZZMUWWbX6eoZvmL76S/GklG8wcViaQIQxcnAJwk28y/Pfy/H/ht4Js+I4H ulO2fTj+x8WCKYVxaR0/d+LdiO6JrWqMDJcUHPdVS1oqGv6b6Phijt/avUfXG/6J/zWd990Gk71 yC3gB X-Developer-Key: i=cyphar@cyphar.com; a=openpgp; fpr=C9C370B246B09F6DBCFC744C34401015D1D2D386 Signed-off-by: Aleksa Sarai --- tools/testing/selftests/clone3/.gitignore | 1 + tools/testing/selftests/clone3/Makefile | 4 +- .../testing/selftests/clone3/clone3_check_fields.c | 264 +++++++++++++++++++++ 3 files changed, 267 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/clone3/.gitignore b/tools/testing/selftests/clone3/.gitignore index 83c0f6246055..4ec3e1ecd273 100644 --- a/tools/testing/selftests/clone3/.gitignore +++ b/tools/testing/selftests/clone3/.gitignore @@ -3,3 +3,4 @@ clone3 clone3_clear_sighand clone3_set_tid clone3_cap_checkpoint_restore +clone3_check_fields diff --git a/tools/testing/selftests/clone3/Makefile b/tools/testing/selftests/clone3/Makefile index 84832c369a2e..37141ca13f7c 100644 --- a/tools/testing/selftests/clone3/Makefile +++ b/tools/testing/selftests/clone3/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 -CFLAGS += -g -std=gnu99 $(KHDR_INCLUDES) +CFLAGS += -g -std=gnu99 $(KHDR_INCLUDES) $(TOOLS_INCLUDES) LDLIBS += -lcap TEST_GEN_PROGS := clone3 clone3_clear_sighand clone3_set_tid \ - clone3_cap_checkpoint_restore + clone3_cap_checkpoint_restore clone3_check_fields include ../lib.mk diff --git a/tools/testing/selftests/clone3/clone3_check_fields.c b/tools/testing/selftests/clone3/clone3_check_fields.c new file mode 100644 index 000000000000..477604f9a3fb --- /dev/null +++ b/tools/testing/selftests/clone3/clone3_check_fields.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Author: Aleksa Sarai + * Copyright (C) 2024 SUSE LLC + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kselftest.h" +#include "clone3_selftests.h" + +#ifndef CHECK_FIELDS +#define CHECK_FIELDS (1ULL << 63) +#endif + +struct __clone_args_v0 { + __aligned_u64 flags; + __aligned_u64 pidfd; + __aligned_u64 child_tid; + __aligned_u64 parent_tid; + __aligned_u64 exit_signal; + __aligned_u64 stack; + __aligned_u64 stack_size; + __aligned_u64 tls; +}; + +struct __clone_args_v1 { + __aligned_u64 flags; + __aligned_u64 pidfd; + __aligned_u64 child_tid; + __aligned_u64 parent_tid; + __aligned_u64 exit_signal; + __aligned_u64 stack; + __aligned_u64 stack_size; + __aligned_u64 tls; + __aligned_u64 set_tid; + __aligned_u64 set_tid_size; +}; + +struct __clone_args_v2 { + __aligned_u64 flags; + __aligned_u64 pidfd; + __aligned_u64 child_tid; + __aligned_u64 parent_tid; + __aligned_u64 exit_signal; + __aligned_u64 stack; + __aligned_u64 stack_size; + __aligned_u64 tls; + __aligned_u64 set_tid; + __aligned_u64 set_tid_size; + __aligned_u64 cgroup; +}; + +static int call_clone3(void *clone_args, size_t size) +{ + int status; + pid_t pid; + + pid = sys_clone3(clone_args, size); + if (pid < 0) + return -errno; + + if (pid == 0) { + ksft_print_msg("I am the child, my PID is %d\n", getpid()); + _exit(EXIT_SUCCESS); + } + + ksft_print_msg("I am the parent (%d). My child's pid is %d\n", + getpid(), pid); + + if (waitpid(-1, &status, __WALL) < 0) { + ksft_print_msg("waitpid() returned %s\n", strerror(errno)); + return -errno; + } + if (!WIFEXITED(status)) { + ksft_print_msg("Child did not exit normally, status 0x%x\n", + status); + return EXIT_FAILURE; + } + if (WEXITSTATUS(status)) + return WEXITSTATUS(status); + + return 0; +} + +static bool check(bool *failed, bool pred) +{ + *failed |= pred; + return pred; +} + +static void test_clone3_check_fields(const char *test_name, size_t struct_size) +{ + size_t bufsize; + void *buffer; + pid_t pid; + bool failed = false; + void (*resultfn)(const char *msg, ...) = ksft_test_result_pass; + + /* Allocate some bytes after clone_args to verify they are cleared. */ + bufsize = struct_size + 16; + buffer = malloc(bufsize); + /* Set the structure to dummy values. */ + memset(buffer, 0, bufsize); + memset(buffer, 0xAB, struct_size); + + pid = call_clone3(buffer, CHECK_FIELDS | struct_size); + if (check(&failed, (pid != -EEXTSYS_NOOP))) + ksft_print_msg("clone3(CHECK_FIELDS) returned the wrong error code: %d (%s) != %d\n", + pid, strerror(-pid), -EEXTSYS_NOOP); + + switch (struct_size) { + case sizeof(struct __clone_args_v2): { + struct __clone_args_v2 *args = buffer; + + if (check(&failed, (args->cgroup != 0xFFFFFFFFFFFFFFFF))) + ksft_print_msg("clone3(CHECK_FIELDS) has wrong cgroup field: 0x%.16llx != 0x%.16llx\n", + args->cgroup, 0xFFFFFFFFFFFFFFFF); + + /* fallthrough; */ + } + case sizeof(struct __clone_args_v1): { + struct __clone_args_v1 *args = buffer; + + if (check(&failed, (args->set_tid != 0xFFFFFFFFFFFFFFFF))) + ksft_print_msg("clone3(CHECK_FIELDS) has wrong set_tid field: 0x%.16llx != 0x%.16llx\n", + args->set_tid, 0xFFFFFFFFFFFFFFFF); + if (check(&failed, (args->set_tid_size != 0xFFFFFFFFFFFFFFFF))) + ksft_print_msg("clone3(CHECK_FIELDS) has wrong set_tid_size field: 0x%.16llx != 0x%.16llx\n", + args->set_tid_size, 0xFFFFFFFFFFFFFFFF); + + /* fallthrough; */ + } + case sizeof(struct __clone_args_v0): { + struct __clone_args_v0 *args = buffer; + + if (check(&failed, !(args->flags & CLONE_NEWUSER))) + ksft_print_msg("clone3(CHECK_FIELDS) is missing CLONE_NEWUSER in flags: 0x%.16llx (0x%.16llx)\n", + args->flags, CLONE_NEWUSER); + if (check(&failed, !(args->flags & CLONE_THREAD))) + ksft_print_msg("clone3(CHECK_FIELDS) is missing CLONE_THREAD in flags: 0x%.16llx (0x%.16llx)\n", + args->flags, CLONE_THREAD); + /* + * CLONE_INTO_CGROUP was added in v2, but it will be set even + * with smaller structure sizes. + */ + if (check(&failed, !(args->flags & CLONE_INTO_CGROUP))) + ksft_print_msg("clone3(CHECK_FIELDS) is missing CLONE_INTO_CGROUP in flags: 0x%.16llx (0x%.16llx)\n", + args->flags, CLONE_INTO_CGROUP); + + if (check(&failed, (args->exit_signal != 0xFF))) + ksft_print_msg("clone3(CHECK_FIELDS) has wrong exit_signal field: 0x%.16llx != 0x%.16llx\n", + args->exit_signal, 0xFF); + + if (check(&failed, (args->stack != 0xFFFFFFFFFFFFFFFF))) + ksft_print_msg("clone3(CHECK_FIELDS) has wrong stack field: 0x%.16llx != 0x%.16llx\n", + args->stack, 0xFFFFFFFFFFFFFFFF); + if (check(&failed, (args->stack_size != 0xFFFFFFFFFFFFFFFF))) + ksft_print_msg("clone3(CHECK_FIELDS) has wrong stack_size field: 0x%.16llx != 0x%.16llx\n", + args->stack_size, 0xFFFFFFFFFFFFFFFF); + if (check(&failed, (args->tls != 0xFFFFFFFFFFFFFFFF))) + ksft_print_msg("clone3(CHECK_FIELDS) has wrong tls field: 0x%.16llx != 0x%.16llx\n", + args->tls, 0xFFFFFFFFFFFFFFFF); + + break; + } + default: + fprintf(stderr, "INVALID STRUCTURE SIZE: %d\n", struct_size); + abort(); + } + + /* Verify that the trailing parts of the buffer are still 0. */ + for (size_t i = struct_size; i < bufsize; i++) { + char ch = ((char *)buffer)[i]; + if (check(&failed, (ch != '\x00'))) + ksft_print_msg("clone3(CHECK_FIELDS) touched a byte outside the size: buffer[%d] = 0x%.2x\n", + i, ch); + } + + if (failed) + resultfn = ksft_test_result_fail; + + resultfn("clone3(CHECK_FIELDS) with %s\n", test_name); + free(buffer); +} + +struct check_fields_test { + const char *name; + size_t struct_size; +}; + +static struct check_fields_test check_fields_tests[] = { + {"struct v0", sizeof(struct __clone_args_v0)}, + {"struct v1", sizeof(struct __clone_args_v1)}, + {"struct v2", sizeof(struct __clone_args_v2)}, +}; + +static int test_clone3_check_fields_badsize(const char *test_name, + size_t struct_size) +{ + void *buffer; + pid_t pid; + bool failed = false; + int expected_err; + void (*resultfn)(const char *msg, ...) = ksft_test_result_pass; + + buffer = malloc(struct_size); + memset(buffer, 0xAB, struct_size); + + if (struct_size < sizeof(struct __clone_args_v0)) + expected_err = -EINVAL; + if (struct_size > 4096) + expected_err = -E2BIG; + + pid = call_clone3(buffer, CHECK_FIELDS | struct_size); + if (check(&failed, (pid != expected_err))) + ksft_print_msg("clone3(CHECK_FIELDS) returned the wrong error code: %d (%s) != %d (%s)\n", + pid, strerror(-pid), + expected_err, strerror(-expected_err)); + + if (failed) + resultfn = ksft_test_result_fail; + + resultfn("clone3(CHECK_FIELDS) with %s returns %d (%s)\n", test_name, + expected_err, strerror(-expected_err)); + free(buffer); +} + +static struct check_fields_test bad_size_tests[] = { + {"short struct (< v0)", 1}, + {"long struct (> PAGE_SIZE)", 0xF000}, +}; + +int main(void) +{ + ksft_print_header(); + ksft_set_plan(ARRAY_SIZE(check_fields_tests) + ARRAY_SIZE(bad_size_tests)); + test_clone3_supported(); + + for (int i = 0; i < ARRAY_SIZE(check_fields_tests); i++) { + struct check_fields_test *test = &check_fields_tests[i]; + test_clone3_check_fields(test->name, test->struct_size); + } + for (int i = 0; i < ARRAY_SIZE(bad_size_tests); i++) { + struct check_fields_test *test = &bad_size_tests[i]; + test_clone3_check_fields_badsize(test->name, test->struct_size); + } + + ksft_finished(); +} From patchwork Thu Sep 5 14:56:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aleksa Sarai X-Patchwork-Id: 13792512 Received: from mout-p-202.mailbox.org (mout-p-202.mailbox.org [80.241.56.172]) (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 7F4B219DFB9; Thu, 5 Sep 2024 14:58:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548315; cv=none; b=szPKpMx99mzMI/bOT6do86GY5QGa8R3SO+X6omaR0++EuhLZQtGGZB5X7gfX7J7KAVP0oH05OXnFGY6rBtmcFNGOJThNmXVxfQabUbRLPoLe+WzcdaXVx7dHHjpeGFDNjbssvulyts8oZtLbO4jCKekkLGU7S7kJN52Zp9+EcJM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548315; c=relaxed/simple; bh=m/kukrXbUb2iR1VdL6T6wbbPdJMNl29jeGapuUY2L5E=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Dmgno5pMTytnSuX3/JGXKFQRDBtLy4a8ic/CDwTqgNLgGLzMkpItuJDqLDO8zWuzE0ISPf3Ce7hFZLRO88e94qq362SaLGBkIRwZPuvbwGsJ0XFsTpjybgqmQ79TPJHHALHVCsO/Ib+w5yAet+g9O8SYn4HqX0kmbsbD374+mOk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com; spf=pass smtp.mailfrom=cyphar.com; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b=h9ABOC4I; arc=none smtp.client-ip=80.241.56.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cyphar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b="h9ABOC4I" Received: from smtp102.mailbox.org (smtp102.mailbox.org [10.196.197.102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-202.mailbox.org (Postfix) with ESMTPS id 4X02YL187Gz9t6V; Thu, 5 Sep 2024 16:58:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cyphar.com; s=MBO0001; t=1725548310; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=u4cJ+jIxm5+97OBbjnL0YlZHGQ1e5+nIetwERzw4CVk=; b=h9ABOC4IWs3rhQ9sv/s3TmMzqPvEviQ5eNZ1cT+uloarFLJwXwwNRPx113OFs/s4yLWBVK hUZACIEbMjWswqP7AJ67HB0xmXH70GTWqhzugLvED1bbpZs0cfRpsYHWQW45wA49qqPmF5 XJsZhCXl6z/ly65zMx1pI9KOLg3Bi9cinRlhMDc+TwcThNZR/dNdx/HoCLdChJY3/8Z4Wl IJJubFY0QzMW6BAof2nXIDj3NjsaTC5D52cEjiBGDfLNs9h95cwaifqwmn+cfHMCO2FnsG Iy+9aOlHXF859S+CLLQWvD3hgop2UKccvRevwDUwgkRaOYcI0Nv12PvhpWFe6w== From: Aleksa Sarai Date: Fri, 06 Sep 2024 00:56:41 +1000 Subject: [PATCH RFC v2 09/10] mount_setattr: add CHECK_FIELDS flag to usize argument Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240906-extensible-structs-check_fields-v2-9-0f46d2de9bad@cyphar.com> References: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> In-Reply-To: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> To: Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Alexander Viro , Christian Brauner , Jan Kara , Arnd Bergmann , Shuah Khan Cc: Kees Cook , Florian Weimer , Arnd Bergmann , Mark Rutland , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, Aleksa Sarai X-Developer-Signature: v=1; a=openpgp-sha256; l=2453; i=cyphar@cyphar.com; h=from:subject:message-id; bh=m/kukrXbUb2iR1VdL6T6wbbPdJMNl29jeGapuUY2L5E=; b=owGbwMvMwCWmMf3Xpe0vXfIZT6slMaTdPLYl7PEi3vcOX9/56+0I+xnwYqdLxGORuw79Gx+8X prcNuO0SkcpC4MYF4OsmCLLNj/P0E3zF19J/rSSDWYOKxPIEAYuTgGYSPpVhv8RCtu35f7aHTCF eeYhf9Mje61npykZz54sdPHU+6+SMQW6DL9Za+99zuy7dqQ9YsbHX8/C90U9WLhM7rdAppCGdaL A0U8MAA== X-Developer-Key: i=cyphar@cyphar.com; a=openpgp; fpr=C9C370B246B09F6DBCFC744C34401015D1D2D386 As with openat2(2), this allows userspace to easily figure out what flags and fields are supported by mount_setattr(2). As with clone3(2), for fields which are not flag-based, we simply set every bit in the field so that a naive bitwise-and would show that any value of the field is valid. The intended way of using this interface to get feature information looks something like the following: static bool mountattr_nosymfollow_supported; static bool mountattr_idmap_supported; int check_clone3_support(void) { int err; struct mount_attr attr = {}; err = mount_attr(-EBADF, "", 0, &args, CHECK_FIELDS | sizeof(args)); assert(err < 0); switch (errno) { case EFAULT: case E2BIG: /* Old kernel... */ check_support_the_old_way(); break; case EEXTSYS_NOOP: mountattr_nosymfollow_supported = ((attr.attr_clr | attr.attr_set) & MOUNT_ATTR_NOSYMFOLLOW); mountattr_idmap_supported = ((attr.attr_clr | attr.attr_set) & MOUNT_ATTR_IDMAP) && (attr.userns_fd != 0); break; } } Signed-off-by: Aleksa Sarai --- fs/namespace.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fs/namespace.c b/fs/namespace.c index 328087a4df8a..c7ae8d96b7b7 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -4771,6 +4771,7 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, size_t, usize) { int err; + bool check_fields; struct path target; struct mount_attr attr; struct mount_kattr kattr; @@ -4783,11 +4784,27 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, AT_NO_AUTOMOUNT)) return -EINVAL; + check_fields = usize & CHECK_FIELDS; + usize &= ~CHECK_FIELDS; + if (unlikely(usize > PAGE_SIZE)) return -E2BIG; if (unlikely(usize < MOUNT_ATTR_SIZE_VER0)) return -EINVAL; + if (unlikely(check_fields)) { + memset(&attr, 0, sizeof(attr)); + attr = (struct mount_attr) { + .attr_set = MOUNT_SETATTR_VALID_FLAGS, + .attr_clr = MOUNT_SETATTR_VALID_FLAGS, + .propagation = MOUNT_SETATTR_PROPAGATION_FLAGS, + .userns_fd = 0xFFFFFFFFFFFFFFFF, + }; + + err = copy_struct_to_user(uattr, usize, &attr, sizeof(attr), NULL); + return err ?: -EEXTSYS_NOOP; + } + if (!may_mount()) return -EPERM; From patchwork Thu Sep 5 14:56:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aleksa Sarai X-Patchwork-Id: 13792513 Received: from mout-p-101.mailbox.org (mout-p-101.mailbox.org [80.241.56.151]) (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 52E4F1A42C8; Thu, 5 Sep 2024 14:58:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=80.241.56.151 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548325; cv=none; b=b5i1nqWw1DUl8J3Qy8IaiOmy0imccMLppYDP/OLHhhiSfayH4PY7h91+CbCMOQSMqYN5gMhkK3E+cavnRrGPnBUFVFzNXW5o3Ni1BqfMIaNu1A3vzSZPJqYqXUnOxKH5mAHRuCAQMJmcuLvZHFMXMNdQa+sAUVLJ2oaEPpIH130= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725548325; c=relaxed/simple; bh=Md2voHWHN6ZD8jB5U2AftXt35HXnXcLeKJYVRv/yU1o=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=A1X7nvJrNvjj5mdS98fW0Zwn9wvtpq5q2E1Qbb5xaPhBdiUNNQRveOTPOJq9nVKkgdeYjdPrXQT7vqLQkeQUPXqQk4J3l57K7r2BPNWb+VRco4OKCnHqsWUI0nbx+escXZBV37IV/H0jHAIcTfriaw2yfm9zM9rw+HZfhFhqF8k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com; spf=pass smtp.mailfrom=cyphar.com; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b=aG00TQEM; arc=none smtp.client-ip=80.241.56.151 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=cyphar.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=cyphar.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=cyphar.com header.i=@cyphar.com header.b="aG00TQEM" Received: from smtp102.mailbox.org (smtp102.mailbox.org [IPv6:2001:67c:2050:b231:465::102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-101.mailbox.org (Postfix) with ESMTPS id 4X02YW4rLQz9smZ; Thu, 5 Sep 2024 16:58:39 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cyphar.com; s=MBO0001; t=1725548319; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Ee2kk+0paPhXmF0WCRx/YdS1aoxGKNnY8mZZOtBU46g=; b=aG00TQEMzHIPzDz6cGwggGf69Wwa5FoZVA8w3GWaZCippzdPPYk9yEFYAl6u/dyxNslSbI NPjV08uf5KI5MF74kWs2n/f0LoqAsUm1HdnW9lsS65rd6unUAIV4yldAKUEUUsPlDtD1gy y6LerCCrHV5CNH+N0+DBDSJhcxaILnfCFe4/qGOJqBAdO7E0n36KFz57lSJXo8ahKcxWsn +JIwa9EpMoypgCuYx3gsdbdiHdZQIa9JzOhZhuJZjT8NYUS5NY0jTxg6aUwuWyX5DMH2O8 Q3p/39VGoys8zv5HRKi5NLBbjK7Vblo70YuboK6SItCz2J+lMSOKhRsjPsdiww== From: Aleksa Sarai Date: Fri, 06 Sep 2024 00:56:42 +1000 Subject: [PATCH RFC v2 10/10] selftests: mount_setattr: add CHECK_FIELDS selftest Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240906-extensible-structs-check_fields-v2-10-0f46d2de9bad@cyphar.com> References: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> In-Reply-To: <20240906-extensible-structs-check_fields-v2-0-0f46d2de9bad@cyphar.com> To: Ingo Molnar , Peter Zijlstra , Juri Lelli , Vincent Guittot , Dietmar Eggemann , Steven Rostedt , Ben Segall , Mel Gorman , Valentin Schneider , Alexander Viro , Christian Brauner , Jan Kara , Arnd Bergmann , Shuah Khan Cc: Kees Cook , Florian Weimer , Arnd Bergmann , Mark Rutland , linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-arch@vger.kernel.org, linux-kselftest@vger.kernel.org, Aleksa Sarai X-Developer-Signature: v=1; a=openpgp-sha256; l=7253; i=cyphar@cyphar.com; h=from:subject:message-id; bh=Md2voHWHN6ZD8jB5U2AftXt35HXnXcLeKJYVRv/yU1o=; b=owGbwMvMwCWmMf3Xpe0vXfIZT6slMaTdPLZF41ZUslpOlGTZDqa5O3ujGf+uNfvsfG5h4gn3/ d8XPXzL0VHKwiDGxSArpsiyzc8zdNP8xVeSP61kg5nDygQyhIGLUwAmUmPHyLDMTNlh2s8T+R+5 35ScYbUX3Vqx70invkOM9e1NifIVNyYzMvQse5EfOPdM4Xo9H2eH7lsa/8t9pXbcVtW4yFE7uTo 9hgMA X-Developer-Key: i=cyphar@cyphar.com; a=openpgp; fpr=C9C370B246B09F6DBCFC744C34401015D1D2D386 X-Rspamd-Queue-Id: 4X02YW4rLQz9smZ While we're at it -- to make testing for error codes with ASSERT_* easier, make the sys_* wrappers return -errno in case of an error. For some reason, the include doesn't correct import , leading to compilation errors -- so just import manually. Signed-off-by: Aleksa Sarai --- tools/include/uapi/asm-generic/posix_types.h | 101 +++++++++++++++++++++ tools/testing/selftests/mount_setattr/Makefile | 2 +- .../selftests/mount_setattr/mount_setattr_test.c | 53 ++++++++++- 3 files changed, 153 insertions(+), 3 deletions(-) diff --git a/tools/include/uapi/asm-generic/posix_types.h b/tools/include/uapi/asm-generic/posix_types.h new file mode 100644 index 000000000000..b5f7594eee7a --- /dev/null +++ b/tools/include/uapi/asm-generic/posix_types.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_GENERIC_POSIX_TYPES_H +#define __ASM_GENERIC_POSIX_TYPES_H + +#include +/* + * This file is generally used by user-level software, so you need to + * be a little careful about namespace pollution etc. + * + * First the types that are often defined in different ways across + * architectures, so that you can override them. + */ + +#ifndef __kernel_long_t +typedef long __kernel_long_t; +typedef unsigned long __kernel_ulong_t; +#endif + +#ifndef __kernel_ino_t +typedef __kernel_ulong_t __kernel_ino_t; +#endif + +#ifndef __kernel_mode_t +typedef unsigned int __kernel_mode_t; +#endif + +#ifndef __kernel_pid_t +typedef int __kernel_pid_t; +#endif + +#ifndef __kernel_ipc_pid_t +typedef int __kernel_ipc_pid_t; +#endif + +#ifndef __kernel_uid_t +typedef unsigned int __kernel_uid_t; +typedef unsigned int __kernel_gid_t; +#endif + +#ifndef __kernel_suseconds_t +typedef __kernel_long_t __kernel_suseconds_t; +#endif + +#ifndef __kernel_daddr_t +typedef int __kernel_daddr_t; +#endif + +#ifndef __kernel_uid32_t +typedef unsigned int __kernel_uid32_t; +typedef unsigned int __kernel_gid32_t; +#endif + +#ifndef __kernel_old_uid_t +typedef __kernel_uid_t __kernel_old_uid_t; +typedef __kernel_gid_t __kernel_old_gid_t; +#endif + +#ifndef __kernel_old_dev_t +typedef unsigned int __kernel_old_dev_t; +#endif + +/* + * Most 32 bit architectures use "unsigned int" size_t, + * and all 64 bit architectures use "unsigned long" size_t. + */ +#ifndef __kernel_size_t +#if __BITS_PER_LONG != 64 +typedef unsigned int __kernel_size_t; +typedef int __kernel_ssize_t; +typedef int __kernel_ptrdiff_t; +#else +typedef __kernel_ulong_t __kernel_size_t; +typedef __kernel_long_t __kernel_ssize_t; +typedef __kernel_long_t __kernel_ptrdiff_t; +#endif +#endif + +#ifndef __kernel_fsid_t +typedef struct { + int val[2]; +} __kernel_fsid_t; +#endif + +/* + * anything below here should be completely generic + */ +typedef __kernel_long_t __kernel_off_t; +typedef long long __kernel_loff_t; +typedef __kernel_long_t __kernel_old_time_t; +#ifndef __KERNEL__ +typedef __kernel_long_t __kernel_time_t; +#endif +typedef long long __kernel_time64_t; +typedef __kernel_long_t __kernel_clock_t; +typedef int __kernel_timer_t; +typedef int __kernel_clockid_t; +typedef char * __kernel_caddr_t; +typedef unsigned short __kernel_uid16_t; +typedef unsigned short __kernel_gid16_t; + +#endif /* __ASM_GENERIC_POSIX_TYPES_H */ diff --git a/tools/testing/selftests/mount_setattr/Makefile b/tools/testing/selftests/mount_setattr/Makefile index 0c0d7b1234c1..3f260506fe60 100644 --- a/tools/testing/selftests/mount_setattr/Makefile +++ b/tools/testing/selftests/mount_setattr/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 # Makefile for mount selftests. -CFLAGS = -g $(KHDR_INCLUDES) -Wall -O2 -pthread +CFLAGS = -g $(KHDR_INCLUDES) $(TOOLS_INCLUDES) -Wall -O2 -pthread TEST_GEN_PROGS := mount_setattr_test diff --git a/tools/testing/selftests/mount_setattr/mount_setattr_test.c b/tools/testing/selftests/mount_setattr/mount_setattr_test.c index c6a8c732b802..36b8286e5f43 100644 --- a/tools/testing/selftests/mount_setattr/mount_setattr_test.c +++ b/tools/testing/selftests/mount_setattr/mount_setattr_test.c @@ -11,6 +11,7 @@ #include #include #include +#include /* get __kernel_* typedefs */ #include #include #include @@ -137,7 +138,8 @@ static inline int sys_mount_setattr(int dfd, const char *path, unsigned int flags, struct mount_attr *attr, size_t size) { - return syscall(__NR_mount_setattr, dfd, path, flags, attr, size); + int ret = syscall(__NR_mount_setattr, dfd, path, flags, attr, size); + return ret >= 0 ? ret : -errno; } #ifndef OPEN_TREE_CLONE @@ -152,9 +154,24 @@ static inline int sys_mount_setattr(int dfd, const char *path, unsigned int flag #define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */ #endif +#define FSMOUNT_VALID_FLAGS \ + (MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NODEV | \ + MOUNT_ATTR_NOEXEC | MOUNT_ATTR__ATIME | MOUNT_ATTR_NODIRATIME | \ + MOUNT_ATTR_NOSYMFOLLOW) + +#define MOUNT_SETATTR_VALID_FLAGS (FSMOUNT_VALID_FLAGS | MOUNT_ATTR_IDMAP) + +#define MOUNT_SETATTR_PROPAGATION_FLAGS \ + (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED) + +#ifndef CHECK_FIELDS +#define CHECK_FIELDS (1ULL << 63) +#endif + static inline int sys_open_tree(int dfd, const char *filename, unsigned int flags) { - return syscall(__NR_open_tree, dfd, filename, flags); + int ret = syscall(__NR_open_tree, dfd, filename, flags); + return ret >= 0 ? ret : -errno; } static ssize_t write_nointr(int fd, const void *buf, size_t count) @@ -1497,4 +1514,36 @@ TEST_F(mount_setattr, mount_attr_nosymfollow) ASSERT_EQ(close(fd), 0); } +TEST_F(mount_setattr, check_fields) +{ + struct mount_attr_ext { + struct mount_attr inner; + uint64_t extra1; + uint64_t extra2; + uint64_t extra3; + } attr; + + /* Set the structure to dummy values. */ + memset(&attr, 0xAB, sizeof(attr)); + + if (!mount_setattr_supported()) + SKIP(return, "mount_setattr syscall not supported"); + + /* Make sure that invalid sizes are detected even with CHECK_FIELDS. */ + EXPECT_EQ(sys_mount_setattr(-1, "", 0, (struct mount_attr *) &attr, CHECK_FIELDS), -EINVAL); + EXPECT_EQ(sys_mount_setattr(-1, "", 0, (struct mount_attr *) &attr, CHECK_FIELDS | 0xF000), -E2BIG); + + /* Actually get the CHECK_FIELDS results. */ + ASSERT_EQ(sys_mount_setattr(-1, "", 0, (struct mount_attr *) &attr, CHECK_FIELDS | sizeof(attr)), -EEXTSYS_NOOP); + + EXPECT_EQ(attr.inner.attr_set, MOUNT_SETATTR_VALID_FLAGS); + EXPECT_EQ(attr.inner.attr_clr, MOUNT_SETATTR_VALID_FLAGS); + EXPECT_EQ(attr.inner.propagation, MOUNT_SETATTR_PROPAGATION_FLAGS); + EXPECT_EQ(attr.inner.userns_fd, 0xFFFFFFFFFFFFFFFF); + /* Trailing fields outside ksize must be zeroed. */ + EXPECT_EQ(attr.extra1, 0); + EXPECT_EQ(attr.extra2, 0); + EXPECT_EQ(attr.extra3, 0); +} + TEST_HARNESS_MAIN