A subtle ABI change was introduced in compat64 by "clone: Alter
clone to accept capabilities". Indeed, since there is no compat
handler for clone, the native one is also used for compat, including
64-bit compat (compat64). This is where the way we convert compat64
syscall arguments to native (PCuABI) is showing its limits: if a
syscall wrapper expects a capability-sized argument, compat_ptr() is
used to convert the user-provided 64-bit value to a capability.
In general, this is what we want, and in fact it is the case for the
parent_tidptr and child_tidptr arguments of clone, which are
ordinary pointers to user data. However, the newsp and tls arguments
are special: they specify the value to set registers to. We should
not alter these values in any way: in arm64/PCuABI, they are
capabilities and we set CSP/CTPIDR accordingly, but in hybrid, they
are still 64-bit values and we should only set the lower 64 bits of
CSP/CTPIDR. This is not the case in compat64 as compat_ptr() is
called to turn these 64-bit values into capabilities.
The most correct solution would be to introduce a compat clone
wrapper, but this is rather painful as clone has 4 possible
prototypes depending on the architecture. The approach taken here is
a middle ground, narrowing down the stack / TLS pointer arguments
in the native handler if we got called from compat. This effectively
cancels out the automatic creation of capabilities in the compat
syscall wrapper, which is not ideal but considered acceptable in
this very particular situation.
Fixes: ("clone: Alter clone to accept capabilities")
Co-developed-by: Beata Michalska <beata.michalska(a)arm.com>
Signed-off-by: Kevin Brodsky <kevin.brodsky(a)arm.com>
---
kernel/fork.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/kernel/fork.c b/kernel/fork.c
index 94777ac4d455..73fe97ad471e 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2743,14 +2743,19 @@ SYSCALL_DEFINE5(clone, unsigned long, clone_flags, user_uintptr_t, newsp,
user_uintptr_t, tls)
#endif
{
+ bool compat_mode = in_compat_syscall();
struct kernel_clone_args args = {
.flags = (lower_32_bits(clone_flags) & ~CSIGNAL),
.pidfd = parent_tidptr,
.child_tid = child_tidptr,
.parent_tid = parent_tidptr,
.exit_signal = (lower_32_bits(clone_flags) & CSIGNAL),
- .stack = newsp,
- .tls = tls,
+ .stack = (compat_mode ?
+ (user_uintptr_t)(compat_ulong_t)newsp :
+ newsp),
+ .tls = (compat_mode ?
+ (user_uintptr_t)(compat_ulong_t)tls :
+ tls),
};
return kernel_clone(&args);
--
2.38.1
A subtle ABI change was introduced in compat64 by "clone: Alter
clone to accept capabilities". Indeed, since there is no compat
handler for clone, the native one is also used for compat64. This is
where the way we convert compat64 syscall arguments to native is
showing its limits: if a syscall wrapper expects a capability-sized
argument, compat_ptr() is used to convert the user-provided 64-bit
value to a capability.
In general, this is what we want, and in fact it is the case for the
parent_tidptr and child_tidptr arguments of clone, which are
ordinary pointers to user data. However, the newsp and tls arguments
are special: they specify the value to set registers to. We should
not alter these values in any way: in PCuABI, they are capabilities
and we set CSP/CTPIDR accordingly, but in hybrid, they are still
64-bit values and we should only set the lower 64 bits of
CSP/CTPIDR. This is not the case in compat64 as compat_ptr() is
called to turn these 64-bit values into capabilities.
The most correct solution would be to introduce a generic compat
clone wrapper, but this is rather painful as clone has 4 possible
prototypes depending on the architecture. Given that the issue is
completely specific to the hybrid ABI, overriding the compat64
syscall wrapper in sys_compat64.c feels like a reasonable
compromise.
Signed-off-by: Kevin Brodsky <kevin.brodsky(a)arm.com>
---
I've realised this inconsistency while thinking about the initialisation
of capability registers ("New CHERI API and separation root
capabilities" series), as well as reviewing the clone3 series. We are
already doing the right thing for clone3, time to align clone.
arch/arm64/kernel/sys_compat64.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/arch/arm64/kernel/sys_compat64.c b/arch/arm64/kernel/sys_compat64.c
index 819b895ec21d..b3c4cf9f3af8 100644
--- a/arch/arm64/kernel/sys_compat64.c
+++ b/arch/arm64/kernel/sys_compat64.c
@@ -9,6 +9,7 @@
#include <linux/compat.h>
#include <linux/compiler.h>
+#include <linux/sched/task.h>
#include <linux/syscalls.h>
#include <asm/syscall.h>
@@ -83,6 +84,33 @@
#define __arm64_compatentry_compat_sys_setitimer __arm64_compatentry_sys_setitimer
#define __arm64_compatentry_compat_sys_getrusage __arm64_compatentry_sys_getrusage
+/*
+ * This is exactly the same definition as the native clone, except that newsp
+ * and tls are defined as unsigned long, not user_uintptr_t. When the native ABI
+ * is PCuABI, this prevents capabilities from being implicitly created for the
+ * stack/TLS in compat64 by the syscall wrapper. This ensures alignment with the
+ * hybrid ABI (i.e. CSP/CTPIDR are set to the 64-bit values passed to clone()).
+ */
+COMPAT_SYSCALL_DEFINE5(arm64_clone, unsigned long, clone_flags, unsigned long, newsp,
+ int __user *, parent_tidptr,
+ unsigned long, tls,
+ int __user *, child_tidptr)
+{
+ struct kernel_clone_args args = {
+ .flags = (lower_32_bits(clone_flags) & ~CSIGNAL),
+ .pidfd = parent_tidptr,
+ .child_tid = child_tidptr,
+ .parent_tid = parent_tidptr,
+ .exit_signal = (lower_32_bits(clone_flags) & CSIGNAL),
+ .stack = newsp,
+ .tls = tls,
+ };
+
+ return kernel_clone(&args);
+}
+
+#define __arm64_compatentry_sys_clone __arm64_compatentry_compat_sys_arm64_clone
+
asmlinkage long sys_ni_syscall(void);
asmlinkage long __arm64_compatentry_sys_ni_syscall(const struct pt_regs *__unused)
--
2.38.1
From: Carsten Haitzler <carsten.haitzler(a)foss.arm.com>
In one case the arg is actually just an int passed into the arg and
was being casted from ptr -> long -> int. Now goes through user_intptr_t.
Signed-off-by: Carsten Haitzler <Carsten.Haitzler(a)arm.com>
---
drivers/input/evdev.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index ea4dab2a53f7..066dd1d8cfe4 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -1074,7 +1074,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return 0;
case EVIOCRMFF:
- return input_ff_erase(dev, (int)(unsigned long) p, file);
+ return input_ff_erase(dev, (int)(user_intptr_t) p, file);
case EVIOCGEFFECTS:
i = test_bit(EV_FF, dev->evbit) ?
--
2.25.1
Enable the required config options to run panfrost/komeda in the
default defconfig for Morello Transitional PureCap User ABI (PCuABI)
(morello_transitional_pcuabi_defconfig).
Note: The series was verified only by CI and at framebuffer level.
Further testing is required to exercise all the components.
To simplify future testing of this series the complete patch set
applied on top of recent morello kernel can be found at [1].
[1] https://git.morello-project.org/vincenzo/linux morello/drm/v1
Co-developed-by: Kevin Brodsky <kevin.brodsky(a)arm.com>
Co-developed-by: Carsten Haitzler <Carsten.Haitzler(a)arm.com>
Signed-off-by: Vincenzo Frascino <vincenzo.frascino(a)arm.com>
Liviu Dudau (1):
drm/komeda: Fix handling of atomic commits in the atomic_commit_tail
hook
Vincenzo Frascino (9):
morello: dt: Add support for the FVP, SoC
Revert "drm/komeda - Fix handling of pending crtc state commit to
avoid lock-up"
drm: drm_legacy: Fix CONFIG_DRM_LEGACY guards in drm_legacy.h
media: cec: Use proper type to represent user pointers
fbdev: Use proper typecast for capability type
drm: i2c: Include hdmi-codec definitions only when required
rtc: Use proper type to represent user pointers
morello: Enable GPU/DPU in defconfig
morello: Enable RTC support
arch/arm64/boot/dts/arm/Makefile | 1 +
arch/arm64/boot/dts/arm/morello-fvp.dts | 171 +++++++++++
arch/arm64/boot/dts/arm/morello-soc.dts | 278 ++++++++++++++++++
arch/arm64/boot/dts/arm/morello.dtsi | 124 ++++++++
.../morello_transitional_pcuabi_defconfig | 11 +
.../gpu/drm/arm/display/komeda/komeda_crtc.c | 14 +-
.../gpu/drm/arm/display/komeda/komeda_kms.c | 40 +--
.../gpu/drm/arm/display/komeda/komeda_kms.h | 5 +-
drivers/gpu/drm/drm_legacy.h | 2 +-
drivers/gpu/drm/i2c/tda998x_drv.c | 13 +
drivers/media/cec/core/cec-api.c | 4 +-
drivers/rtc/dev.c | 4 +-
drivers/video/fbdev/core/fbmem.c | 7 +-
13 files changed, 633 insertions(+), 41 deletions(-)
create mode 100644 arch/arm64/boot/dts/arm/morello-fvp.dts
create mode 100644 arch/arm64/boot/dts/arm/morello-soc.dts
create mode 100644 arch/arm64/boot/dts/arm/morello.dtsi
--
2.39.0
as_user_ptr() is intended to be used where an arbitrary integer e.g. an
error code is stored in a __user ptr.
The current implementation can be somewhat confusing in that it looks
like it can be used as direct replacement for u64_to_user_ptr e.g. in
PCuABI, where u64 addresses in the kernel-user interface are being
replaced with capability containing types such as __kernel_uintptr_t.
Currently, passing a valid capability e.g. a __kernel_uintptr_t,
__uintcap_t or user_uintptr_t etc to as_user_ptr() will result in a cast
to (void __user *) and a valid capability/pointer that can be
dereferenced. This is contrary to the code comment and intended usage
for as_user_ptr().
Add a cast to (u64) before the cast to (void __user *)(user_uintptr_t)
to make clearer the intended usage. This also always results in a null
capability that cannot be dereferenced.
Signed-off-by: Zachary Leaf <zachary.leaf(a)arm.com>
---
rendered version:
https://git.morello-project.org/zdleaf/linux/-/blob/docs/as_user_ptr_v3/Doc…
Documentation/core-api/user_ptr.rst | 16 +++++++++++++---
include/linux/user_ptr.h | 6 +++---
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/Documentation/core-api/user_ptr.rst b/Documentation/core-api/user_ptr.rst
index 21e02d4bd11b..197bb198ede7 100644
--- a/Documentation/core-api/user_ptr.rst
+++ b/Documentation/core-api/user_ptr.rst
@@ -31,7 +31,8 @@ errors are likely to occur in PCuABI if it is omitted.
In certain situations, it is more convenient to represent user pointers
as integers. The type ``user_uintptr_t`` must be used for that purpose.
It is **the only integer type** that may be directly cast to and from a
-user pointer, for instance ``user_uintptr_t u = (user_uintptr_t)uptr``.
+user pointer, for instance ``user_uintptr_t uint = (user_uintptr_t)uptr``
+or ``void __user *uptr = (void __user *)uint``.
Note that ``(u)intptr_t`` is the recommended type to represent kernel
pointers, but it cannot represent user pointers.
@@ -106,6 +107,13 @@ Each function covers a particular category of input integer:
- Integer of any type: ``as_user_ptr()``
- ``u64`` (deprecated): ``u64_to_user_ptr()``
+Note: ``as_user_ptr()`` nullifies any capability and is not a
+replacement for most uses of ``u64_to_user_ptr()``. To convert an
+integer representation of a user pointer i.e. ``user_uintptr_t`` back to
+pointer type, a simple cast such as ``(void __user *)`` is sufficient.
+See `Representing user pointers`_ and notes for ``as_user_ptr()`` and
+``u64_to_user_ptr()`` below.
+
These functions are available in ``<linux/user_ptr.h>``, except
``compat_ptr()`` (``<linux/compat.h>``).
@@ -142,8 +150,10 @@ derived from in the PCuABI case.
| | | ``compat_*`` struct | | |
+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+
| ``as_user_ptr()`` | Arbitrary integer | Error code | Null capability | This is a pure representation change, as suggested |
-| | | | | by the ``as_`` prefix. The resulting pointer cannot |
-| | | | | be dereferenced. |
+| | | | | by the ``as_`` prefix. Returns up to 64 bits of an |
+| | | | | arbitrary integer represented as a user pointer. The |
+| | | | | result is not a valid pointer and cannot be |
+| | | | | dereferenced. |
+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+
| ``u64_to_user_ptr()`` | ``u64`` integer | [Deprecated] | Null capability | Legacy function, new callers should not be added. |
| | | | | Existing callers should move to either |
diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h
index 0942b58cfb6a..516024f3fead 100644
--- a/include/linux/user_ptr.h
+++ b/include/linux/user_ptr.h
@@ -12,10 +12,10 @@
* as_user_ptr - convert an arbitrary integer value to a user pointer
* @x: the integer value to convert
*
- * Returns @x represented as a user pointer. The result is not a valid pointer
- * and shall not be dereferenced.
+ * Returns up to 64 bits of @x represented as a user pointer. The result is
+ * not a valid pointer and shall not be dereferenced.
*/
-#define as_user_ptr(x) ((void __user *)(user_uintptr_t)(x))
+#define as_user_ptr(x) ((void __user *)(user_uintptr_t)(u64)(x))
/* Same semantics as as_user_ptr(), but also requires x to be of a given type */
#define as_user_ptr_strict(type, x) ( \
--
2.34.1
as_user_ptr() is intended to be used where an arbitrary integer e.g. an
error code is stored in a __user ptr.
The current implementation can be somewhat confusing in that it looks
like it can be used as direct replacement for u64_to_user_ptr e.g. in
PCuABI, where u64 addresses in the kernel-user interface are being
replaced with capability containing types such as __kernel_uintptr_t.
Currently, passing a valid capability e.g. a __kernel_uintptr_t,
__uintcap_t or user_uintptr_t etc to as_user_ptr() will result in a cast
to (void __user *) and a valid capability/pointer that can be
dereferenced. This is contrary to the code comment and intended usage
for as_user_ptr().
Add a cast to (u64) before the cast to (void __user *)(user_uintptr_t)
to make clearer the intended usage. This also always results in a null
capability that cannot be dereferenced.
Signed-off-by: Zachary Leaf <zachary.leaf(a)arm.com>
---
rendered version:
https://git.morello-project.org/zdleaf/linux/-/blob/dev/as_user_ptr/Documen…
Documentation/core-api/user_ptr.rst | 22 +++++++++++++++++-----
include/linux/user_ptr.h | 6 +++---
2 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/Documentation/core-api/user_ptr.rst b/Documentation/core-api/user_ptr.rst
index 21e02d4bd11b..b4b9afe88095 100644
--- a/Documentation/core-api/user_ptr.rst
+++ b/Documentation/core-api/user_ptr.rst
@@ -31,7 +31,8 @@ errors are likely to occur in PCuABI if it is omitted.
In certain situations, it is more convenient to represent user pointers
as integers. The type ``user_uintptr_t`` must be used for that purpose.
It is **the only integer type** that may be directly cast to and from a
-user pointer, for instance ``user_uintptr_t u = (user_uintptr_t)uptr``.
+user pointer, for instance ``user_uintptr_t uint = (user_uintptr_t)uptr``
+or ``void __user *uptr = (void __user *)uint``.
Note that ``(u)intptr_t`` is the recommended type to represent kernel
pointers, but it cannot represent user pointers.
@@ -106,6 +107,13 @@ Each function covers a particular category of input integer:
- Integer of any type: ``as_user_ptr()``
- ``u64`` (deprecated): ``u64_to_user_ptr()``
+Note: ``as_user_ptr()`` nullifies any capability and is not a
+replacement for all uses of ``u64_to_user_ptr()``. To convert an integer
+representation of a user pointer i.e. user_uintptr_t back to pointer
+type, a simple cast such as ``(void __user *)`` is sufficient. See
+`Representing user pointers`_ and notes for ``as_user_ptr()`` and
+``u64_to_user_ptr()`` below.
+
These functions are available in ``<linux/user_ptr.h>``, except
``compat_ptr()`` (``<linux/compat.h>``).
@@ -142,16 +150,20 @@ derived from in the PCuABI case.
| | | ``compat_*`` struct | | |
+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+
| ``as_user_ptr()`` | Arbitrary integer | Error code | Null capability | This is a pure representation change, as suggested |
-| | | | | by the ``as_`` prefix. The resulting pointer cannot |
-| | | | | be dereferenced. |
+| | | | | by the ``as_`` prefix. Returns up to 64 bits of an |
+| | | | | arbitrary integer represented as a user pointer. The |
+| | | | | result is not a valid pointer and cannot be |
+| | | | | dereferenced. |
+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+
| ``u64_to_user_ptr()`` | ``u64`` integer | [Deprecated] | Null capability | Legacy function, new callers should not be added. |
| | | | | Existing callers should move to either |
| | | | | ``as_user_ptr()`` if the user pointer is not used to |
| | | | | access memory, or ``uaddr_to_user_ptr()`` if the |
| | | | | input is an address and the user pointer is |
-| | | | | dereferenced (or ideally removed if the uapi can be |
-| | | | | changed appropriately). |
+| | | | | dereferenced (or ideally replace u64 with a |
+| | | | | capability containing type such as |
+| | | | | ``__kernel_uintptr_t`` if the uapi can be changed |
+| | | | | appropriately). |
+------------------------------+--------------------+------------------------+-----------------------------------+------------------------------------------------------+
diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h
index 0942b58cfb6a..516024f3fead 100644
--- a/include/linux/user_ptr.h
+++ b/include/linux/user_ptr.h
@@ -12,10 +12,10 @@
* as_user_ptr - convert an arbitrary integer value to a user pointer
* @x: the integer value to convert
*
- * Returns @x represented as a user pointer. The result is not a valid pointer
- * and shall not be dereferenced.
+ * Returns up to 64 bits of @x represented as a user pointer. The result is
+ * not a valid pointer and shall not be dereferenced.
*/
-#define as_user_ptr(x) ((void __user *)(user_uintptr_t)(x))
+#define as_user_ptr(x) ((void __user *)(user_uintptr_t)(u64)(x))
/* Same semantics as as_user_ptr(), but also requires x to be of a given type */
#define as_user_ptr_strict(type, x) ( \
--
2.34.1
Commit 01b2a5217173 ("tracing: Add ioctl() to force ring buffer waiters
to wake up") added a callback to unlocked_ioctl, however in PCuABI the
function signature of file_operations->unlocked_ioctl in
include/linux/fs.h has been changed to take a user_uintptr_t arg as the
third param.
Update the signature of tracing_buffers_ioctl to avoid compilation
errors as below when tracing is enabled.
kernel/trace/trace.c:8396:20: error: incompatible function pointer types
initializing 'long (*)(struct file *, unsigned int, user_uintptr_t)'
(aka 'long (*)(struct file *, unsigned int, unsigned __intcap)') with an
expression of type 'long (struct file *, unsigned int, unsigned long)'
[-Werror,-Wincompatible-function-pointer-types]
Reported-by: Teo Couprie Diaz <teo.coupriediaz(a)arm.com>
Signed-off-by: Zachary Leaf <zachary.leaf(a)arm.com>
---
note: this patch is for incoming rebase from 5.18->6.1
kernel/trace/trace.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 5cfc95a52bc3..3ad1db4c4a3e 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -8367,7 +8367,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
}
/* An ioctl call with cmd 0 to the ring buffer file will wake up all waiters */
-static long tracing_buffers_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static long tracing_buffers_ioctl(struct file *file, unsigned int cmd, user_uintptr_t arg)
{
struct ftrace_buffer_info *info = file->private_data;
struct trace_iterator *iter = &info->iter;
--
2.34.1