s390/uaccess: Prevent kprobes on cmpxchg_user_key() functions

Code regions within cmpxchg_user_key() functions may be executed with a
non-default access key, which may lead to a protection exception if the
corresponding page has the fetch-protection bit enabled.

There is code in place which initializes the storage keys of such pages
when needed. However there is also the possibility of out-of-line execution
of such code in case a kprobe is set within such a region.

To avoid this problem prevent that any kprobe can be set within the
cmpxchg_user_key() functions.

Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
This commit is contained in:
Heiko Carstens 2025-06-16 17:00:32 +02:00 committed by Alexander Gordeev
parent b13c190c6d
commit d2b73ce90a

View File

@ -8,6 +8,7 @@
* Gerald Schaefer (gerald.schaefer@de.ibm.com)
*/
#include <linux/kprobes.h>
#include <linux/uaccess.h>
#include <linux/export.h>
#include <linux/mm.h>
@ -149,8 +150,8 @@ EXPORT_SYMBOL(_copy_to_user_key);
#define CMPXCHG_USER_KEY_MAX_LOOPS 128
int __cmpxchg_user_key1(unsigned long address, unsigned char *uval,
unsigned char old, unsigned char new, unsigned long key)
int __kprobes __cmpxchg_user_key1(unsigned long address, unsigned char *uval,
unsigned char old, unsigned char new, unsigned long key)
{
unsigned int prev, shift, mask, _old, _new;
unsigned long count;
@ -208,8 +209,8 @@ int __cmpxchg_user_key1(unsigned long address, unsigned char *uval,
}
EXPORT_SYMBOL(__cmpxchg_user_key1);
int __cmpxchg_user_key2(unsigned long address, unsigned short *uval,
unsigned short old, unsigned short new, unsigned long key)
int __kprobes __cmpxchg_user_key2(unsigned long address, unsigned short *uval,
unsigned short old, unsigned short new, unsigned long key)
{
unsigned int prev, shift, mask, _old, _new;
unsigned long count;
@ -267,8 +268,8 @@ int __cmpxchg_user_key2(unsigned long address, unsigned short *uval,
}
EXPORT_SYMBOL(__cmpxchg_user_key2);
int __cmpxchg_user_key4(unsigned long address, unsigned int *uval,
unsigned int old, unsigned int new, unsigned long key)
int __kprobes __cmpxchg_user_key4(unsigned long address, unsigned int *uval,
unsigned int old, unsigned int new, unsigned long key)
{
unsigned int prev = old;
bool sacf_flag;
@ -299,8 +300,8 @@ int __cmpxchg_user_key4(unsigned long address, unsigned int *uval,
}
EXPORT_SYMBOL(__cmpxchg_user_key4);
int __cmpxchg_user_key8(unsigned long address, unsigned long *uval,
unsigned long old, unsigned long new, unsigned long key)
int __kprobes __cmpxchg_user_key8(unsigned long address, unsigned long *uval,
unsigned long old, unsigned long new, unsigned long key)
{
unsigned long prev = old;
bool sacf_flag;
@ -331,8 +332,8 @@ int __cmpxchg_user_key8(unsigned long address, unsigned long *uval,
}
EXPORT_SYMBOL(__cmpxchg_user_key8);
int __cmpxchg_user_key16(unsigned long address, __uint128_t *uval,
__uint128_t old, __uint128_t new, unsigned long key)
int __kprobes __cmpxchg_user_key16(unsigned long address, __uint128_t *uval,
__uint128_t old, __uint128_t new, unsigned long key)
{
__uint128_t prev = old;
bool sacf_flag;