summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-20 14:28:32 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-20 14:28:32 -1000
commit375d5d57545f53d2bc3e15ada0e9da360fc68bea (patch)
tree5c8a03a7fc1eeace002f03a31ee4a70f9990a8ea /drivers
parentae3af2d105b224e91fd5abefd96af5f9760e1744 (diff)
parent3e30b96fab780110f62156d5a8cd5240152b3c59 (diff)
Merge tag 's390-7.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 updates from Heiko Carstens: - Add support for sclp-vt220 terminal resize events e.g. for QEMU - Warn if kernel command line contains non-printable EBCDIC characters - Improve show_code() disassembly output by using reliable markers for the instruction address and faulting instruction - Rework exception handling to require only a single exception table entry per faulting instruction: convert all inline assemblies from EX_TABLE to the new EX_TABLE_INSN macros. The previous exception handling was error prone since it usually required two EX_TABLE entries: one pointing at the instruction, and one pointing behind it - Disallow kprobes on instructions with exception table entries to avoid exception handlers decoding a breakpoint instead of the original instruction - Preemptible percpu ops changes: - Simplify percpu_entry() and percpu_exit() by encoding the percpu base, offset, and pointer registers into the lowcore marker, allowing percpu_exit() to update the percpu pointer on CPU migration without comparing the interrupted instruction - Convert this_cpu_cmpxchg(), this_cpu_xchg(), and this_cpu_cmpxchg128() to preemptible percpu ops; provide this_cpu_try_cmpxchg() - Emulate virtual timer in appldata via delayed work and remove the s390 specific vtimer infrastructure - Add lock contention tracepoints to rwlock slowpath - Fix incorrect return address in signal frame setup - Fix cpum_cf CPU hotplug handling - Convert various __get_free_pages() / get_zeroed_page() users to kmalloc() / kzalloc() - PCI: - Improve RPCIT instruction tracing - Fix double-free, use-after-free, and resource leaks in zPCI MSI and IRQ setup / cleanup paths - Crypto and zcrypt: - Improve reply message and length verification checks for CCA, EP11, and ICA messages to prevent out-of-bounds reads - Add missing array_index_nospec() for domain index uses in zcrypt to prevent speculative execution bypass - Fix out-of-bounds read in cca_query_crypto_facility() - Various other small fixes and improvements * tag 's390-7.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (102 commits) s390/signal: Cleanup coding style s390/signal: Fix incorrect return address in signal frame s390/probes: Fix wrong mnemonic comment for clgib s390/probes: Add missing fixup for bctgr and bic s390/probes: Fix wrong opcode for cgrb fixup s390: Select ARCH_MEMORY_ORDER_TSO s390/mm: Cleanup pXXp_flush_lazy() routines s390/pci: Add function handle to RPCIT insn trace s390/pci: Use 32-bit fh outside of inline asm s390/pci: Adjust alignment in insn trace s390/sclp: Remove redundant memset after memblock_alloc_low() s390/entry: Save lowcore on invalid kernel stack s390/entry: Last breaking event vs invalid kernel stack s390/percpu: ImplemenHeiko Carstenst this_cpu_try_cmpxchg() s390/percpu: Drop unused output register from arch_this_cpu_to_op() s390/percpu: Drop unused output register from arch_this_cpu_add() s390/percpu: Use asm_inline for arch_this_cpu_add() s390/extable: Remove EX_TABLE macros and fallback extable search s390/bpf: Simplify exception table entry handling s390/sclp: Convert inline assembly to EX_TABLE_INSN ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/iommu/s390-iommu.c8
-rw-r--r--drivers/s390/block/dasd_diag.c6
-rw-r--r--drivers/s390/char/diag_ftp.c6
-rw-r--r--drivers/s390/char/fs3270.c3
-rw-r--r--drivers/s390/char/keyboard.c6
-rw-r--r--drivers/s390/char/sclp.h7
-rw-r--r--drivers/s390/char/sclp_early.c1
-rw-r--r--drivers/s390/char/sclp_vt220.c87
-rw-r--r--drivers/s390/char/vmcp.c4
-rw-r--r--drivers/s390/cio/chsc.c20
-rw-r--r--drivers/s390/cio/chsc_sch.c300
-rw-r--r--drivers/s390/cio/cmf.c11
-rw-r--r--drivers/s390/cio/ioasm.c24
-rw-r--r--drivers/s390/cio/qdio.h2
-rw-r--r--drivers/s390/cio/qdio_main.c23
-rw-r--r--drivers/s390/cio/qdio_setup.c14
-rw-r--r--drivers/s390/cio/qdio_thinint.c2
-rw-r--r--drivers/s390/cio/scm.c7
-rw-r--r--drivers/s390/crypto/zcrypt_api.c35
-rw-r--r--drivers/s390/crypto/zcrypt_ccamisc.c22
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.c95
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.c202
22 files changed, 513 insertions, 372 deletions
diff --git a/drivers/iommu/s390-iommu.c b/drivers/iommu/s390-iommu.c
index 58ca7727b7f2..a280ad4f63b7 100644
--- a/drivers/iommu/s390-iommu.c
+++ b/drivers/iommu/s390-iommu.c
@@ -788,7 +788,7 @@ static struct iommu_device *s390_iommu_probe_device(struct device *dev)
static int zpci_refresh_all(struct zpci_dev *zdev)
{
- return zpci_refresh_trans((u64)zdev->fh << 32, zdev->start_dma,
+ return zpci_refresh_trans(zdev->fh, zdev->start_dma,
zdev->end_dma - zdev->start_dma + 1);
}
@@ -819,8 +819,7 @@ static void s390_iommu_iotlb_sync(struct iommu_domain *domain,
rcu_read_lock();
list_for_each_entry_rcu(zdev, &s390_domain->devices, iommu_list) {
atomic64_inc(&s390_domain->ctrs.sync_rpcits);
- zpci_refresh_trans((u64)zdev->fh << 32, gather->start,
- size);
+ zpci_refresh_trans(zdev->fh, gather->start, size);
}
rcu_read_unlock();
}
@@ -837,8 +836,7 @@ static int s390_iommu_iotlb_sync_map(struct iommu_domain *domain,
if (!zdev->tlb_refresh)
continue;
atomic64_inc(&s390_domain->ctrs.sync_map_rpcits);
- ret = zpci_refresh_trans((u64)zdev->fh << 32,
- iova, size);
+ ret = zpci_refresh_trans(zdev->fh, iova, size);
/*
* let the hypervisor discover invalidated entries
* allowing it to free IOVAs and unpin pages
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index af68c2ee659d..ce391d35adf9 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -77,11 +77,11 @@ static inline int __dia250(void *iob, int cmd)
exception = 1;
asm_inline volatile(
- " diag %[rx],%[cmd],0x250\n"
- "0: lhi %[exc],0\n"
+ "0: diag %[rx],%[cmd],0x250\n"
+ " lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
- EX_TABLE(0b,1b)
+ EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [rx] "+d" (rx.pair),
"+m" (*(addr_type *)iob), [exc] "+d" (exception)
: [cmd] "d" (cmd)
diff --git a/drivers/s390/char/diag_ftp.c b/drivers/s390/char/diag_ftp.c
index a1e110c96f74..0a506559799e 100644
--- a/drivers/s390/char/diag_ftp.c
+++ b/drivers/s390/char/diag_ftp.c
@@ -106,11 +106,11 @@ static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl,
diag_stat_inc(DIAG_STAT_X2C4);
asm_inline volatile(
- " diag %[addr],%[cmd],0x2c4\n"
- "0: j 2f\n"
+ "0: diag %[addr],%[cmd],0x2c4\n"
+ " j 2f\n"
"1: la %[rc],%[err]\n"
"2:\n"
- EX_TABLE(0b, 1b)
+ EX_TABLE_INSN(0b, 1b)
: [rc] "=d" (rc), "+m" (*fpl)
: [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)),
[err] "i" (DIAG_FTP_RET_EPERM)
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index f8e2cc17ac53..25f0a85d92fa 100644
--- a/drivers/s390/char/fs3270.c
+++ b/drivers/s390/char/fs3270.c
@@ -355,6 +355,9 @@ static long fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (copy_to_user(argp, &iocb, sizeof(struct raw3270_iocb)))
rc = -EFAULT;
break;
+ default:
+ rc = -ENOTTY;
+ break;
}
mutex_unlock(&fs3270_mutex);
return rc;
diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c
index ecd9ace2b3aa..63bcb52d6e87 100644
--- a/drivers/s390/char/keyboard.c
+++ b/drivers/s390/char/keyboard.c
@@ -81,7 +81,8 @@ kbd_alloc(void) {
kbd = kzalloc_obj(struct kbd_data);
if (!kbd)
goto out;
- kbd->key_maps = kzalloc(sizeof(ebc_key_maps), GFP_KERNEL);
+ kbd->key_maps = kcalloc(ARRAY_SIZE(ebc_key_maps),
+ sizeof(*kbd->key_maps), GFP_KERNEL);
if (!kbd->key_maps)
goto out_kbd;
for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
@@ -93,7 +94,8 @@ kbd_alloc(void) {
goto out_maps;
}
}
- kbd->func_table = kzalloc(sizeof(ebc_func_table), GFP_KERNEL);
+ kbd->func_table = kcalloc(ARRAY_SIZE(ebc_func_table),
+ sizeof(*kbd->func_table), GFP_KERNEL);
if (!kbd->func_table)
goto out_maps;
for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++) {
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index b31a680e0871..b91d31f8e0ef 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -320,11 +320,10 @@ static inline int sclp_service_call(sclp_cmdw_t command, void *sccb)
exception = 1;
asm_inline volatile(
"0: .insn rre,0xb2200000,%[cmd],%[sccb]\n" /* servc */
- "1: lhi %[exc],0\n"
- "2:\n"
+ " lhi %[exc],0\n"
+ "1:\n"
CC_IPM(cc)
- EX_TABLE(0b, 2b)
- EX_TABLE(1b, 2b)
+ EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [exc] "+d" (exception)
: [cmd] "d" (command), [sccb] "a" (__pa(sccb))
: CC_CLOBBER_LIST("memory"));
diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c
index 22dd797e6229..821b61727d1f 100644
--- a/drivers/s390/char/sclp_early.c
+++ b/drivers/s390/char/sclp_early.c
@@ -138,7 +138,6 @@ int __init sclp_early_get_core_info(struct sclp_core_info *info)
if (!sccb)
return -ENOMEM;
- memset(sccb, 0, length);
sccb->header.length = length;
sccb->header.control_mask[2] = 0x80;
if (sclp_early_cmd(SCLP_CMDW_READ_CPU_INFO, sccb)) {
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index 4e2bf8186a5d..b95af0974624 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -27,6 +27,7 @@
#include <linux/init.h>
#include <linux/reboot.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <linux/uaccess.h>
#include "sclp.h"
@@ -52,6 +53,17 @@ struct sclp_vt220_sccb {
struct evbuf_header evbuf;
};
+struct sclp_vt220_evbuf {
+ struct evbuf_header header;
+ char type;
+ char data[];
+} __packed;
+
+struct sclp_vt220_resize_data_t {
+ u16 rows;
+ u16 cols;
+} __packed;
+
#define SCLP_VT220_MAX_CHARS_PER_BUFFER (PAGE_SIZE - \
sizeof(struct sclp_vt220_request) - \
sizeof(struct sclp_vt220_sccb))
@@ -89,6 +101,15 @@ static int __initdata sclp_vt220_init_count;
* another buffer */
static int sclp_vt220_flush_later;
+/* Work struct required for scheduling resize */
+static struct work_struct sclp_vt220_resize_work;
+
+/* Current vt220 terminal winsize */
+static struct winsize sclp_vt220_winsize = {
+ .ws_row = 24,
+ .ws_col = 80,
+};
+
static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf);
static int __sclp_vt220_emit(struct sclp_vt220_request *request);
static void sclp_vt220_emit_current(void);
@@ -468,6 +489,7 @@ sclp_vt220_write(struct tty_struct *tty, const u8 *buf, size_t count)
#define SCLP_VT220_SESSION_ENDED 0x01
#define SCLP_VT220_SESSION_STARTED 0x80
#define SCLP_VT220_SESSION_DATA 0x00
+#define SCLP_VT220_SESSION_RESIZE 0x08
#ifdef CONFIG_MAGIC_SYSRQ
@@ -516,30 +538,65 @@ static void sclp_vt220_handle_input(const char *buffer, unsigned int count)
#endif
+static void sclp_vt220_resize(struct work_struct *work)
+{
+ struct tty_struct *tty;
+ struct winsize ws;
+
+ spin_lock_irq(&sclp_vt220_lock);
+ ws = sclp_vt220_winsize;
+ spin_unlock_irq(&sclp_vt220_lock);
+
+ tty = tty_port_tty_get(&sclp_vt220_port);
+ if (!tty)
+ return;
+
+ tty_do_resize(tty, &ws);
+ tty_kref_put(tty);
+}
+
+static void sclp_vt220_resize_sched(void *buffer, unsigned int count)
+{
+ struct sclp_vt220_resize_data_t *data = buffer;
+ unsigned long flags;
+
+ if (count < sizeof(*data))
+ return;
+
+ spin_lock_irqsave(&sclp_vt220_lock, flags);
+ sclp_vt220_winsize.ws_row = data->rows;
+ sclp_vt220_winsize.ws_col = data->cols;
+ schedule_work(&sclp_vt220_resize_work);
+ spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+}
+
/*
* Called by the SCLP to report incoming event buffers.
*/
-static void
-sclp_vt220_receiver_fn(struct evbuf_header *evbuf)
+static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf)
{
- char *buffer;
+ struct sclp_vt220_evbuf *buffer;
unsigned int count;
- buffer = (char *) ((addr_t) evbuf + sizeof(struct evbuf_header));
- count = evbuf->length - sizeof(struct evbuf_header);
+ if (evbuf->length < offsetof(struct sclp_vt220_evbuf, data))
+ return;
+
+ buffer = (struct sclp_vt220_evbuf *)evbuf;
+ count = evbuf->length - offsetof(struct sclp_vt220_evbuf, data);
- switch (*buffer) {
+ switch (buffer->type) {
case SCLP_VT220_SESSION_ENDED:
case SCLP_VT220_SESSION_STARTED:
sclp_vt220_reset_session();
break;
case SCLP_VT220_SESSION_DATA:
/* Send input to line discipline */
- buffer++;
- count--;
- sclp_vt220_handle_input(buffer, count);
+ sclp_vt220_handle_input(buffer->data, count);
tty_flip_buffer_push(&sclp_vt220_port);
break;
+ case SCLP_VT220_SESSION_RESIZE:
+ sclp_vt220_resize_sched(buffer->data, count);
+ break;
}
}
@@ -551,10 +608,12 @@ sclp_vt220_open(struct tty_struct *tty, struct file *filp)
{
if (tty->count == 1) {
tty_port_tty_set(&sclp_vt220_port, tty);
- if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
- tty->winsize.ws_row = 24;
- tty->winsize.ws_col = 80;
- }
+
+ mutex_lock(&tty->winsize_mutex);
+ spin_lock_irq(&sclp_vt220_lock);
+ tty->winsize = sclp_vt220_winsize;
+ spin_unlock_irq(&sclp_vt220_lock);
+ mutex_unlock(&tty->winsize_mutex);
}
return 0;
}
@@ -747,6 +806,8 @@ static int __init sclp_vt220_tty_init(void)
rc = tty_register_driver(driver);
if (rc)
goto out_init;
+
+ INIT_WORK(&sclp_vt220_resize_work, sclp_vt220_resize);
rc = sclp_register(&sclp_vt220_register_input);
if (rc)
goto out_reg;
diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c
index b277f9d0c321..2cf94f23af2a 100644
--- a/drivers/s390/char/vmcp.c
+++ b/drivers/s390/char/vmcp.c
@@ -104,13 +104,11 @@ static int vmcp_open(struct inode *inode, struct file *file)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- session = kmalloc_obj(*session);
+ session = kzalloc_obj(*session);
if (!session)
return -ENOMEM;
session->bufsize = PAGE_SIZE;
- session->response = NULL;
- session->resp_size = 0;
mutex_init(&session->mutex);
file->private_data = session;
return nonseekable_open(inode, file);
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 9689f722c863..d98f629e8929 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -931,12 +931,12 @@ static int cub_alloc(struct channel_subsystem *css)
int i;
for (i = 0; i < CSS_NUM_CUB_PAGES; i++) {
- css->cub[i] = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ css->cub[i] = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!css->cub[i])
return -ENOMEM;
}
for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
- css->ecub[i] = (void *)get_zeroed_page(GFP_KERNEL);
+ css->ecub[i] = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!css->ecub[i])
return -ENOMEM;
}
@@ -949,11 +949,11 @@ static void cub_free(struct channel_subsystem *css)
int i;
for (i = 0; i < CSS_NUM_CUB_PAGES; i++) {
- free_page((unsigned long)css->cub[i]);
+ kfree(css->cub[i]);
css->cub[i] = NULL;
}
for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
- free_page((unsigned long)css->ecub[i]);
+ kfree(css->ecub[i]);
css->ecub[i] = NULL;
}
}
@@ -1142,8 +1142,8 @@ int __init chsc_init(void)
{
int ret;
- sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sei_page = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
+ chsc_page = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sei_page || !chsc_page) {
ret = -ENOMEM;
goto out_err;
@@ -1153,16 +1153,16 @@ int __init chsc_init(void)
goto out_err;
return ret;
out_err:
- free_page((unsigned long)chsc_page);
- free_page((unsigned long)sei_page);
+ kfree(chsc_page);
+ kfree(sei_page);
return ret;
}
void __init chsc_init_cleanup(void)
{
crw_unregister_handler(CRW_RSC_CSS);
- free_page((unsigned long)chsc_page);
- free_page((unsigned long)sei_page);
+ kfree(chsc_page);
+ kfree(sei_page);
}
int __chsc_enable_facility(struct chsc_sda_area *sda_area, int operation_code)
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index b6cb8bb8bcc4..225aaac7dd30 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -15,6 +15,7 @@
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/kernel_stat.h>
+#include <linux/cleanup.h>
#include <asm/cio.h>
#include <asm/chsc.h>
@@ -292,7 +293,7 @@ static int chsc_ioctl_start(void __user *user_area)
if (!css_general_characteristics.dynio)
/* It makes no sense to try. */
return -EOPNOTSUPP;
- chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
+ chsc_area = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
if (!chsc_area)
return -ENOMEM;
request = kzalloc_obj(*request);
@@ -321,7 +322,7 @@ out_free:
snprintf(dbf, sizeof(dbf), "ret:%d", ret);
CHSC_LOG(0, dbf);
kfree(request);
- free_page((unsigned long)chsc_area);
+ kfree(chsc_area);
return ret;
}
@@ -340,7 +341,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
ret = -ENOMEM;
goto out_unlock;
}
- on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
+ on_close_chsc_area = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
if (!on_close_chsc_area) {
ret = -ENOMEM;
goto out_free_request;
@@ -353,7 +354,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
goto out_unlock;
out_free_chsc:
- free_page((unsigned long)on_close_chsc_area);
+ kfree(on_close_chsc_area);
on_close_chsc_area = NULL;
out_free_request:
kfree(on_close_request);
@@ -375,7 +376,7 @@ static int chsc_ioctl_on_close_remove(void)
ret = -ENOENT;
goto out_unlock;
}
- free_page((unsigned long)on_close_chsc_area);
+ kfree(on_close_chsc_area);
on_close_chsc_area = NULL;
kfree(on_close_request);
on_close_request = NULL;
@@ -389,39 +390,29 @@ out_unlock:
static int chsc_ioctl_start_sync(void __user *user_area)
{
- struct chsc_sync_area *chsc_area;
- int ret, ccode;
+ struct chsc_sync_area *chsc_area __free(kfree) = NULL;
+ int ccode;
- chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ chsc_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!chsc_area)
return -ENOMEM;
- if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
- ret = -EFAULT;
- goto out_free;
- }
- if (chsc_area->header.code & 0x4000) {
- ret = -EINVAL;
- goto out_free;
- }
+ if (copy_from_user(chsc_area, user_area, PAGE_SIZE))
+ return -EFAULT;
+ if (chsc_area->header.code & 0x4000)
+ return -EINVAL;
chsc_log_command(chsc_area);
ccode = chsc(chsc_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (copy_to_user(user_area, chsc_area, PAGE_SIZE))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- free_page((unsigned long)chsc_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_info_channel_path(void __user *user_cd)
{
- struct chsc_chp_cd *cd;
- int ret, ccode;
+ struct chsc_chp_cd *cd __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 : 2;
@@ -436,20 +427,16 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
u32 : 32;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
- } __attribute__ ((packed)) *scpcd_area;
+ } __attribute__ ((packed)) *scpcd_area __free(kfree) = NULL;
- scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ scpcd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scpcd_area)
return -ENOMEM;
cd = kzalloc_obj(*cd);
- if (!cd) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(cd, user_cd, sizeof(*cd))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!cd)
+ return -ENOMEM;
+ if (copy_from_user(cd, user_cd, sizeof(*cd)))
+ return -EFAULT;
scpcd_area->request.length = 0x0010;
scpcd_area->request.code = 0x0028;
scpcd_area->m = cd->m;
@@ -459,31 +446,23 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
scpcd_area->last_chpid = cd->chpid.id;
ccode = chsc(scpcd_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (scpcd_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "scpcd: response code=%x\n",
scpcd_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&cd->cpcb, &scpcd_area->response, scpcd_area->response.length);
if (copy_to_user(user_cd, cd, sizeof(*cd)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(cd);
- free_page((unsigned long)scpcd_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_info_cu(void __user *user_cd)
{
- struct chsc_cu_cd *cd;
- int ret, ccode;
+ struct chsc_cu_cd *cd __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 : 2;
@@ -498,20 +477,16 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
u32 : 32;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
- } __attribute__ ((packed)) *scucd_area;
+ } __attribute__ ((packed)) *scucd_area __free(kfree) = NULL;
- scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ scucd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scucd_area)
return -ENOMEM;
cd = kzalloc_obj(*cd);
- if (!cd) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(cd, user_cd, sizeof(*cd))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!cd)
+ return -ENOMEM;
+ if (copy_from_user(cd, user_cd, sizeof(*cd)))
+ return -EFAULT;
scucd_area->request.length = 0x0010;
scucd_area->request.code = 0x0026;
scucd_area->m = cd->m;
@@ -521,31 +496,23 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
scucd_area->last_cun = cd->cun;
ccode = chsc(scucd_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (scucd_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "scucd: response code=%x\n",
scucd_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&cd->cucb, &scucd_area->response, scucd_area->response.length);
if (copy_to_user(user_cd, cd, sizeof(*cd)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(cd);
- free_page((unsigned long)scucd_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_info_sch_cu(void __user *user_cud)
{
- struct chsc_sch_cud *cud;
- int ret, ccode;
+ struct chsc_sch_cud *cud __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 : 2;
@@ -561,20 +528,16 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
u32 : 32;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
- } __attribute__ ((packed)) *sscud_area;
+ } __attribute__ ((packed)) *sscud_area __free(kfree) = NULL;
- sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sscud_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sscud_area)
return -ENOMEM;
cud = kzalloc_obj(*cud);
- if (!cud) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(cud, user_cud, sizeof(*cud))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!cud)
+ return -ENOMEM;
+ if (copy_from_user(cud, user_cud, sizeof(*cud)))
+ return -EFAULT;
sscud_area->request.length = 0x0010;
sscud_area->request.code = 0x0006;
sscud_area->m = cud->schid.m;
@@ -585,31 +548,23 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
sscud_area->last_sch = cud->schid.sch_no;
ccode = chsc(sscud_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (sscud_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "sscud: response code=%x\n",
sscud_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&cud->scub, &sscud_area->response, sscud_area->response.length);
if (copy_to_user(user_cud, cud, sizeof(*cud)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(cud);
- free_page((unsigned long)sscud_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_conf_info(void __user *user_ci)
{
- struct chsc_conf_info *ci;
- int ret, ccode;
+ struct chsc_conf_info *ci __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 : 2;
@@ -623,20 +578,16 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
u64 : 64;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
- } __attribute__ ((packed)) *sci_area;
+ } __attribute__ ((packed)) *sci_area __free(kfree) = NULL;
- sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sci_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sci_area)
return -ENOMEM;
ci = kzalloc_obj(*ci);
- if (!ci) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(ci, user_ci, sizeof(*ci))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!ci)
+ return -ENOMEM;
+ if (copy_from_user(ci, user_ci, sizeof(*ci)))
+ return -EFAULT;
sci_area->request.length = 0x0010;
sci_area->request.code = 0x0012;
sci_area->m = ci->id.m;
@@ -645,31 +596,23 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
sci_area->ssid = ci->id.ssid;
ccode = chsc(sci_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (sci_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "sci: response code=%x\n",
sci_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&ci->scid, &sci_area->response, sci_area->response.length);
if (copy_to_user(user_ci, ci, sizeof(*ci)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(ci);
- free_page((unsigned long)sci_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
{
- struct chsc_comp_list *ccl;
- int ret, ccode;
+ struct chsc_comp_list *ccl __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 ctype : 8;
@@ -681,7 +624,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
u64 : 64;
struct chsc_header response;
u8 data[PAGE_SIZE - 36];
- } __attribute__ ((packed)) *sccl_area;
+ } __attribute__ ((packed)) *sccl_area __free(kfree) = NULL;
struct {
u32 m : 1;
u32 : 31;
@@ -696,18 +639,14 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
u32 res;
} __attribute__ ((packed)) *cssids_parm;
- sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sccl_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sccl_area)
return -ENOMEM;
ccl = kzalloc_obj(*ccl);
- if (!ccl) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(ccl, user_ccl, sizeof(*ccl))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!ccl)
+ return -ENOMEM;
+ if (copy_from_user(ccl, user_ccl, sizeof(*ccl)))
+ return -EFAULT;
sccl_area->request.length = 0x0020;
sccl_area->request.code = 0x0030;
sccl_area->fmt = ccl->req.fmt;
@@ -728,61 +667,46 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
break;
}
ccode = chsc(sccl_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (sccl_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "sccl: response code=%x\n",
sccl_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&ccl->sccl, &sccl_area->response, sccl_area->response.length);
if (copy_to_user(user_ccl, ccl, sizeof(*ccl)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(ccl);
- free_page((unsigned long)sccl_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_chpd(void __user *user_chpd)
{
- struct chsc_scpd *scpd_area;
- struct chsc_cpd_info *chpd;
+ struct chsc_scpd *scpd_area __free(kfree) = NULL;
+ struct chsc_cpd_info *chpd __free(kfree) = NULL;
int ret;
chpd = kzalloc_obj(*chpd);
- scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!scpd_area || !chpd) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) {
- ret = -EFAULT;
- goto out_free;
- }
+ scpd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!scpd_area || !chpd)
+ return -ENOMEM;
+ if (copy_from_user(chpd, user_chpd, sizeof(*chpd)))
+ return -EFAULT;
ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt,
chpd->rfmt, chpd->c, chpd->m,
scpd_area);
if (ret)
- goto out_free;
+ return ret;
memcpy(&chpd->chpdb, &scpd_area->response, scpd_area->response.length);
if (copy_to_user(user_chpd, chpd, sizeof(*chpd)))
- ret = -EFAULT;
-out_free:
- kfree(chpd);
- free_page((unsigned long)scpd_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static int chsc_ioctl_dcal(void __user *user_dcal)
{
- struct chsc_dcal *dcal;
- int ret, ccode;
+ struct chsc_dcal *dcal __free(kfree) = NULL;
+ int ccode;
struct {
struct chsc_header request;
u32 atype : 8;
@@ -794,20 +718,16 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
u32 res1[2];
struct chsc_header response;
u8 data[PAGE_SIZE - 36];
- } __attribute__ ((packed)) *sdcal_area;
+ } __attribute__ ((packed)) *sdcal_area __free(kfree) = NULL;
- sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sdcal_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sdcal_area)
return -ENOMEM;
dcal = kzalloc_obj(*dcal);
- if (!dcal) {
- ret = -ENOMEM;
- goto out_free;
- }
- if (copy_from_user(dcal, user_dcal, sizeof(*dcal))) {
- ret = -EFAULT;
- goto out_free;
- }
+ if (!dcal)
+ return -ENOMEM;
+ if (copy_from_user(dcal, user_dcal, sizeof(*dcal)))
+ return -EFAULT;
sdcal_area->request.length = 0x0020;
sdcal_area->request.code = 0x0034;
sdcal_area->atype = dcal->req.atype;
@@ -816,26 +736,18 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
sizeof(sdcal_area->list_parm));
ccode = chsc(sdcal_area);
- if (ccode != 0) {
- ret = -EIO;
- goto out_free;
- }
+ if (ccode != 0)
+ return -EIO;
if (sdcal_area->response.code != 0x0001) {
- ret = -EIO;
CHSC_MSG(0, "sdcal: response code=%x\n",
sdcal_area->response.code);
- goto out_free;
+ return -EIO;
}
memcpy(&dcal->sdcal, &sdcal_area->response,
sdcal_area->response.length);
if (copy_to_user(user_dcal, dcal, sizeof(*dcal)))
- ret = -EFAULT;
- else
- ret = 0;
-out_free:
- kfree(dcal);
- free_page((unsigned long)sdcal_area);
- return ret;
+ return -EFAULT;
+ return 0;
}
static long chsc_ioctl(struct file *filp, unsigned int cmd,
@@ -904,7 +816,7 @@ static int chsc_release(struct inode *inode, struct file *filp)
}
snprintf(dbf, sizeof(dbf), "relret:%d", ret);
CHSC_LOG(0, dbf);
- free_page((unsigned long)on_close_chsc_area);
+ kfree(on_close_chsc_area);
on_close_chsc_area = NULL;
kfree(on_close_request);
on_close_request = NULL;
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 66b14fedbd18..02b0feb9d1fd 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -482,6 +482,9 @@ static int alloc_cmb(struct ccw_device *cdev)
ssize_t size;
struct cmb_data *cmb_data;
+ if (cmb_area.num_channels <= 0)
+ return -ENOMEM;
+
/* Allocate private cmb_data. */
cmb_data = kzalloc_obj(struct cmb_data);
if (!cmb_data)
@@ -501,12 +504,12 @@ static int alloc_cmb(struct ccw_device *cdev)
WARN_ON(!list_empty(&cmb_area.list));
spin_unlock(&cmb_area.lock);
- mem = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
+ mem = kmalloc(size, GFP_KERNEL);
spin_lock(&cmb_area.lock);
if (cmb_area.mem) {
/* ok, another thread was faster */
- free_pages((unsigned long)mem, get_order(size));
+ kfree(mem);
} else if (!mem) {
/* no luck */
ret = -ENOMEM;
@@ -547,10 +550,8 @@ static void free_cmb(struct ccw_device *cdev)
list_del_init(&priv->cmb_list);
if (list_empty(&cmb_area.list)) {
- ssize_t size;
- size = sizeof(struct cmb) * cmb_area.num_channels;
cmf_activate(NULL, CMF_OFF);
- free_pages((unsigned long)cmb_area.mem, get_order(size));
+ kfree(cmb_area.mem);
cmb_area.mem = NULL;
}
spin_unlock_irq(cdev->ccwlock);
diff --git a/drivers/s390/cio/ioasm.c b/drivers/s390/cio/ioasm.c
index 8b06b234e110..4974e80b0568 100644
--- a/drivers/s390/cio/ioasm.c
+++ b/drivers/s390/cio/ioasm.c
@@ -24,11 +24,11 @@ static inline int __stsch(struct subchannel_id schid, struct schib *addr)
exception = 1;
asm_inline volatile(
" lgr 1,%[r1]\n"
- " stsch %[addr]\n"
- "0: lhi %[exc],0\n"
+ "0: stsch %[addr]\n"
+ " lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
- EX_TABLE(0b, 1b)
+ EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, ccode), [addr] "=Q" (*addr), [exc] "+d" (exception)
: [r1] "d" (r1)
: CC_CLOBBER_LIST("1"));
@@ -54,11 +54,11 @@ static inline int __msch(struct subchannel_id schid, struct schib *addr)
exception = 1;
asm_inline volatile(
" lgr 1,%[r1]\n"
- " msch %[addr]\n"
- "0: lhi %[exc],0\n"
+ "0: msch %[addr]\n"
+ " lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
- EX_TABLE(0b, 1b)
+ EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, ccode), [exc] "+d" (exception)
: [r1] "d" (r1), [addr] "Q" (*addr)
: CC_CLOBBER_LIST("1"));
@@ -108,11 +108,11 @@ static inline int __ssch(struct subchannel_id schid, union orb *addr)
exception = 1;
asm_inline volatile(
" lgr 1,%[r1]\n"
- " ssch %[addr]\n"
- "0: lhi %[exc],0\n"
+ "0: ssch %[addr]\n"
+ " lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
- EX_TABLE(0b, 1b)
+ EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, ccode), [exc] "+d" (exception)
: [r1] "d" (r1), [addr] "Q" (*addr)
: CC_CLOBBER_LIST("memory", "1"));
@@ -179,11 +179,11 @@ int chsc(void *chsc_area)
exception = 1;
asm_inline volatile(
- " .insn rre,0xb25f0000,%[chsc_area],0\n"
- "0: lhi %[exc],0\n"
+ "0: .insn rre,0xb25f0000,%[chsc_area],0\n"
+ " lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
- EX_TABLE(0b, 1b)
+ EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), "+m" (*(addr_type *)chsc_area), [exc] "+d" (exception)
: [chsc_area] "d" (chsc_area)
: CC_CLOBBER);
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 4bd4c00c9c0c..dff5f53a8795 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -244,7 +244,7 @@ struct qdio_irq {
int perf_stat_enabled;
struct qdr *qdr;
- unsigned long chsc_page;
+ void *chsc_page;
struct qdio_q *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct qdio_q *output_qs[QDIO_MAX_QUEUES_PER_IRQ];
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index c1e09fa34e77..889f63f94a4a 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -12,11 +12,11 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/kmemleak.h>
#include <linux/delay.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/atomic.h>
+#include <linux/slab.h>
#include <asm/debug.h>
#include <asm/qdio.h>
#include <asm/asm.h>
@@ -933,10 +933,10 @@ int qdio_free(struct ccw_device *cdev)
mutex_unlock(&irq_ptr->setup_mutex);
qdio_free_queues(irq_ptr);
- free_page((unsigned long) irq_ptr->qdr);
- free_page(irq_ptr->chsc_page);
+ kfree(irq_ptr->qdr);
+ kfree(irq_ptr->chsc_page);
kfree(irq_ptr->ccw);
- free_page((unsigned long) irq_ptr);
+ kfree(irq_ptr);
return 0;
}
EXPORT_SYMBOL_GPL(qdio_free);
@@ -961,7 +961,7 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
no_output_qs > QDIO_MAX_QUEUES_PER_IRQ)
return -EINVAL;
- irq_ptr = (void *) get_zeroed_page(GFP_KERNEL);
+ irq_ptr = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!irq_ptr)
return -ENOMEM;
@@ -969,9 +969,6 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
if (!irq_ptr->ccw)
goto err_ccw;
- /* kmemleak doesn't scan the page-allocated irq_ptr: */
- kmemleak_not_leak(irq_ptr->ccw);
-
irq_ptr->cdev = cdev;
mutex_init(&irq_ptr->setup_mutex);
if (qdio_allocate_dbf(irq_ptr))
@@ -986,12 +983,12 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
* qdio_establish. In case of low memory and swap on a zfcp disk
* we may not be able to allocate memory otherwise.
*/
- irq_ptr->chsc_page = get_zeroed_page(GFP_KERNEL);
+ irq_ptr->chsc_page = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!irq_ptr->chsc_page)
goto err_chsc;
/* qdr is used in ccw1.cda which is u32 */
- irq_ptr->qdr = (struct qdr *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ irq_ptr->qdr = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!irq_ptr->qdr)
goto err_qdr;
@@ -1004,14 +1001,14 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
return 0;
err_queues:
- free_page((unsigned long) irq_ptr->qdr);
+ kfree(irq_ptr->qdr);
err_qdr:
- free_page(irq_ptr->chsc_page);
+ kfree(irq_ptr->chsc_page);
err_chsc:
err_dbf:
kfree(irq_ptr->ccw);
err_ccw:
- free_page((unsigned long) irq_ptr);
+ kfree(irq_ptr);
return rc;
}
EXPORT_SYMBOL_GPL(qdio_allocate);
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index ea09aadaae4e..e1c5c6a8b3b2 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -35,7 +35,7 @@ void qdio_free_buffers(struct qdio_buffer **buf, unsigned int count)
int pos;
for (pos = 0; pos < count; pos += QBUFF_PER_PAGE)
- free_page((unsigned long) buf[pos]);
+ kfree(buf[pos]);
}
EXPORT_SYMBOL_GPL(qdio_free_buffers);
@@ -49,7 +49,7 @@ int qdio_alloc_buffers(struct qdio_buffer **buf, unsigned int count)
int pos;
for (pos = 0; pos < count; pos += QBUFF_PER_PAGE) {
- buf[pos] = (void *) get_zeroed_page(GFP_KERNEL);
+ buf[pos] = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf[pos]) {
qdio_free_buffers(buf, count);
return -ENOMEM;
@@ -83,7 +83,7 @@ static void __qdio_free_queues(struct qdio_q **queues, unsigned int count)
for (i = 0; i < count; i++) {
q = queues[i];
- free_page((unsigned long)q->sl_page);
+ kfree(q->sl_page);
kmem_cache_free(qdio_q_cache, q);
}
}
@@ -109,7 +109,7 @@ static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues)
return -ENOMEM;
}
- q->sl_page = (void *)__get_free_page(GFP_KERNEL);
+ q->sl_page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!q->sl_page) {
kmem_cache_free(qdio_q_cache, q);
__qdio_free_queues(irq_ptr_qs, i);
@@ -249,11 +249,11 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
DBF_EVENT("getssqd:%4x", schid->sch_no);
if (!irq_ptr) {
- ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL);
+ ssqd = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ssqd)
return -ENOMEM;
} else {
- ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
+ ssqd = irq_ptr->chsc_page;
}
rc = chsc_ssqd(*schid, ssqd);
@@ -270,7 +270,7 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
out:
if (!irq_ptr)
- free_page((unsigned long)ssqd);
+ kfree(ssqd);
return rc;
}
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index e167aa75c3df..a2ff51887537 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -136,7 +136,7 @@ static struct airq_struct tiqdio_airq = {
static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
{
- struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page;
+ struct chsc_scssc_area *scssc = irq_ptr->chsc_page;
dma64_t summary_indicator_addr, subchannel_indicator_addr;
int rc;
diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c
index 171212a6d2d9..b79fe023ab8d 100644
--- a/drivers/s390/cio/scm.c
+++ b/drivers/s390/cio/scm.c
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/err.h>
+#include <linux/cleanup.h>
#include <asm/eadm.h>
#include "chsc.h"
@@ -224,12 +225,12 @@ static int scm_add(struct chsc_scm_info *scm_info, size_t num)
int scm_update_information(void)
{
- struct chsc_scm_info *scm_info;
+ struct chsc_scm_info *scm_info __free(kfree) = NULL;
u64 token = 0;
size_t num;
int ret;
- scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+ scm_info = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scm_info)
return -ENOMEM;
@@ -250,8 +251,6 @@ int scm_update_information(void)
token = scm_info->restok;
} while (token);
- free_page((unsigned long)scm_info);
-
return ret;
}
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index ec6a4c2f9f04..80435fd8e6ad 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -854,7 +854,7 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms,
struct ica_xcRB *xcrb)
{
bool userspace = xflags & ZCRYPT_XFLAG_USERSPACE;
- unsigned int card, domain, func_code = 0;
+ unsigned int card, domain, _dom, func_code = 0;
unsigned int wgt = 0, pref_wgt = 0;
struct zcrypt_queue *zq, *pref_zq;
struct zcrypt_card *zc, *pref_zc;
@@ -877,10 +877,17 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms,
print_hex_dump_debug("ccareq: ", DUMP_PREFIX_ADDRESS, 16, 1,
ap_msg.msg, ap_msg.len, false);
+ /* Check domain for either AUTOSEL_DOM or in range 0...AP_DOMAINS-1 */
+ if (domain != AUTOSEL_DOM && domain >= AP_DOMAINS) {
+ rc = -EINVAL;
+ goto out;
+ }
+ /* Spectre-v1: sanitize domain unconditionally for later use */
+ _dom = array_index_nospec(domain, AP_DOMAINS);
+
if (perms != &ap_perms && domain < AP_DOMAINS) {
if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
- domain = array_index_nospec(domain, AP_DOMAINS);
- if (!test_bit_inv(domain, perms->adm)) {
+ if (!test_bit_inv(_dom, perms->adm)) {
rc = -ENODEV;
goto out;
}
@@ -893,10 +900,11 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms,
* If a valid target domain is set and this domain is NOT a usage
* domain but a control only domain, autoselect target domain.
*/
- if (domain < AP_DOMAINS &&
- !ap_test_config_usage_domain(domain) &&
- ap_test_config_ctrl_domain(domain))
- domain = AUTOSEL_DOM;
+ if (domain < AP_DOMAINS) {
+ if (!ap_test_config_usage_domain(_dom) &&
+ ap_test_config_ctrl_domain(_dom))
+ domain = AUTOSEL_DOM;
+ }
pref_zc = NULL;
pref_zq = NULL;
@@ -1041,7 +1049,7 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms,
struct ep11_target_dev *targets = NULL;
unsigned short target_num;
unsigned int wgt = 0, pref_wgt = 0;
- unsigned int func_code = 0, domain;
+ unsigned int func_code = 0, domain, _dom;
struct ap_message ap_msg;
int cpen, qpen, qid = 0, rc;
struct module *mod;
@@ -1078,10 +1086,17 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms,
print_hex_dump_debug("ep11req: ", DUMP_PREFIX_ADDRESS, 16, 1,
ap_msg.msg, ap_msg.len, false);
+ /* Check domain for either AUTOSEL_DOM or in range 0...AP_DOMAINS-1 */
+ if (domain != AUTOSEL_DOM && domain >= AP_DOMAINS) {
+ rc = -EINVAL;
+ goto out;
+ }
+ /* Spectre-v1: sanitize domain unconditionally for later use */
+ _dom = array_index_nospec(domain, AP_DOMAINS);
+
if (perms != &ap_perms && domain < AP_DOMAINS) {
if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
- domain = array_index_nospec(domain, AP_DOMAINS);
- if (!test_bit_inv(domain, perms->adm)) {
+ if (!test_bit_inv(_dom, perms->adm)) {
rc = -ENODEV;
goto out;
}
diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c
index 19909bf43dc9..862947431164 100644
--- a/drivers/s390/crypto/zcrypt_ccamisc.c
+++ b/drivers/s390/crypto/zcrypt_ccamisc.c
@@ -1627,6 +1627,7 @@ int cca_query_crypto_facility(u16 cardnr, u16 domain,
u8 subfunc_code[2];
u8 lvdata[];
} __packed * prepparm;
+ size_t datalen;
/* get already prepared memory for 2 cprbs with param block each */
rc = alloc_and_prep_cprbmem(parmbsize, &mem,
@@ -1673,27 +1674,44 @@ int cca_query_crypto_facility(u16 cardnr, u16 domain,
prepcblk->rpl_parmb = (u8 __user *)ptr;
prepparm = (struct fqrepparm *)ptr;
ptr = prepparm->lvdata;
+ datalen = parmbsize - 2 * sizeof(u8);
/* check and possibly copy reply rule array */
len = *((u16 *)ptr);
+ if (len > datalen) {
+ ZCRYPT_DBF_ERR("%s reply rule array len %u exceeds datalen %zu\n",
+ __func__, len, datalen);
+ rc = -EIO;
+ goto out;
+ }
+ datalen -= sizeof(u16);
+ ptr += sizeof(u16);
if (len > sizeof(u16)) {
- ptr += sizeof(u16);
len -= sizeof(u16);
if (rarray && rarraylen && *rarraylen > 0) {
*rarraylen = (len > *rarraylen ? *rarraylen : len);
memcpy(rarray, ptr, *rarraylen);
}
+ datalen -= len;
ptr += len;
}
/* check and possible copy reply var array */
len = *((u16 *)ptr);
+ if (len > datalen) {
+ ZCRYPT_DBF_ERR("%s reply var array len %u exceeds datalen %zu\n",
+ __func__, len, datalen);
+ rc = -EIO;
+ goto out;
+ }
+ datalen -= sizeof(u16);
+ ptr += sizeof(u16);
if (len > sizeof(u16)) {
- ptr += sizeof(u16);
len -= sizeof(u16);
if (varray && varraylen && *varraylen > 0) {
*varraylen = (len > *varraylen ? *varraylen : len);
memcpy(varray, ptr, *varraylen);
}
+ datalen -= len;
ptr += len;
}
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.c b/drivers/s390/crypto/zcrypt_msgtype50.c
index d6fc2d8e7fad..5907fb2a6920 100644
--- a/drivers/s390/crypto/zcrypt_msgtype50.c
+++ b/drivers/s390/crypto/zcrypt_msgtype50.c
@@ -152,6 +152,11 @@ struct type80_hdr {
unsigned char reserved3[8];
} __packed;
+struct type80_reply {
+ struct type80_hdr hdr;
+ char data[];
+} __packed;
+
int get_rsa_modex_fc(struct ica_rsa_modexpo *mex, int *fcode)
{
if (!mex->inputdatalength)
@@ -340,32 +345,43 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_queue *zq,
* @data: pointer to user output data
* @length: size of user output data
*
- * Returns 0 on success or -EFAULT.
+ * Returns 0 on success or neg. errno value on failure.
*/
static int convert_type80(struct zcrypt_queue *zq,
struct ap_message *reply,
char __user *outputdata,
unsigned int outputdatalength)
{
- struct type80_hdr *t80h = reply->msg;
- unsigned char *data;
+ struct type80_reply *msg = reply->msg;
+ size_t payload_len;
+
+ /*
+ * reply->len is always >= sizeof(struct type80_reply) here and
+ * outputdatalength (the modulus size) is guaranteed to be equal to
+ * inputdatalength and 0 < outputdatalength <= CEX3A_MAX_MOD_SIZE,
+ * also payload size may be >= outputdatalength.
+ */
+
+ payload_len = reply->len - sizeof(msg->hdr);
- if (t80h->len < sizeof(*t80h) + outputdatalength) {
- /* The result is too short, the CEXxA card may not do that.. */
+ if (outputdatalength > payload_len) {
+ /* We expect at least outputdatalength bytes, broken card ? */
zq->online = 0;
pr_err("Crypto dev=%02x.%04x code=0x%02x => online=0 rc=EAGAIN\n",
AP_QID_CARD(zq->queue->qid),
- AP_QID_QUEUE(zq->queue->qid), t80h->code);
+ AP_QID_QUEUE(zq->queue->qid), msg->hdr.code);
ZCRYPT_DBF_ERR("%s dev=%02x.%04x code=0x%02x => online=0 rc=EAGAIN\n",
__func__, AP_QID_CARD(zq->queue->qid),
- AP_QID_QUEUE(zq->queue->qid), t80h->code);
+ AP_QID_QUEUE(zq->queue->qid), msg->hdr.code);
ap_send_online_uevent(&zq->queue->ap_dev, zq->online);
return -EAGAIN;
}
- BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE);
- data = reply->msg + t80h->len - outputdatalength;
- if (copy_to_user(outputdata, data, outputdatalength))
+
+ if (copy_to_user(outputdata,
+ msg->data + payload_len - outputdatalength,
+ outputdatalength))
return -EFAULT;
+
return 0;
}
@@ -374,14 +390,19 @@ static int convert_response(struct zcrypt_queue *zq,
char __user *outputdata,
unsigned int outputdatalength)
{
- /* Response type byte is the second byte in the response. */
- unsigned char rtype = ((unsigned char *)reply->msg)[1];
+ struct type80_reply *msg = reply->msg;
+
+ /* reply->len is always >= sizeof(struct error_hdr) here */
- switch (rtype) {
+ switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
return convert_error(zq, reply);
case TYPE80_RSP_CODE:
+ if (msg->hdr.code)
+ return convert_error(zq, reply);
+ if (reply->len < sizeof(struct type80_reply))
+ return -EINVAL;
return convert_type80(zq, reply,
outputdata, outputdatalength);
default: /* Unknown response type, this should NEVER EVER happen */
@@ -389,11 +410,11 @@ static int convert_response(struct zcrypt_queue *zq,
pr_err("Crypto dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n",
AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid),
- (int)rtype);
+ (int)msg->hdr.type);
ZCRYPT_DBF_ERR(
"%s dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n",
__func__, AP_QID_CARD(zq->queue->qid),
- AP_QID_QUEUE(zq->queue->qid), (int)rtype);
+ AP_QID_QUEUE(zq->queue->qid), (int)msg->hdr.type);
ap_send_online_uevent(&zq->queue->ap_dev, zq->online);
return -EAGAIN;
}
@@ -416,26 +437,46 @@ static void zcrypt_msgtype50_receive(struct ap_queue *aq,
.reply_code = REP82_ERROR_MACHINE_FAILURE,
};
struct type80_hdr *t80h;
- int len;
+ size_t len;
/* Copy the reply message to the request message buffer. */
if (!reply)
goto out; /* ap_msg->rc indicates the error */
+
t80h = reply->msg;
- if (t80h->type == TYPE80_RSP_CODE) {
- len = t80h->len;
- if (len > reply->bufsize || len > msg->bufsize ||
- len != reply->len) {
- pr_debug("len mismatch => EMSGSIZE\n");
- msg->rc = -EMSGSIZE;
+
+ if (reply->len < sizeof(*t80h) ||
+ t80h->type != TYPE80_RSP_CODE) {
+ if (reply->len < sizeof(error_reply)) {
+ /* total broken reply, use static error reply instead */
+ memcpy(msg->msg, &error_reply, sizeof(error_reply));
+ msg->len = sizeof(error_reply);
goto out;
+ } else {
+ /* malformed reply, convert function will handle this */
+ len = reply->len;
+ goto copy_len_and_out;
}
- memcpy(msg->msg, reply->msg, len);
- msg->len = len;
- } else {
- memcpy(msg->msg, reply->msg, sizeof(error_reply));
- msg->len = sizeof(error_reply);
}
+
+ len = t80h->len;
+
+copy_len_and_out:
+ if (len != reply->len) {
+ msg->rc = -EMSGSIZE;
+ pr_debug("len %zu rpl.len %zu mismatch, msg.rc=%d\n",
+ len, reply->len, msg->rc);
+ goto out;
+ }
+ if (len > reply->bufsize || len > msg->bufsize) {
+ msg->rc = -EMSGSIZE;
+ pr_debug("len %zu exceeds buf %zu/%zu, msg.rc=%d\n",
+ len, reply->bufsize, msg->bufsize, msg->rc);
+ goto out;
+ }
+ memcpy(msg->msg, reply->msg, len);
+ msg->len = len;
+
out:
complete(&msg->response.work);
}
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index 3df1d676de5d..5962224bfb8b 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -766,6 +766,15 @@ static int convert_type86_rng(struct zcrypt_queue *zq,
if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0)
return -EINVAL;
+ /*
+ * Note that offset2 and count2 have already been checked in
+ * zcrypt_msgtype6_receive(). So only check for valid count2
+ * and for not exceeding the hard coded rng buffer size.
+ */
+ if (!msg->fmt2.count2)
+ return -EINVAL;
+ if (msg->fmt2.count2 > ZCRYPT_RNG_BUFFER_SIZE)
+ return -EMSGSIZE;
memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2);
return msg->fmt2.count2;
}
@@ -777,11 +786,17 @@ static int convert_response_ica(struct zcrypt_queue *zq,
{
struct type86x_reply *msg = reply->msg;
+ /* reply->len is always >= sizeof(struct error_hdr) here */
+
switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
return convert_error(zq, reply);
case TYPE86_RSP_CODE:
+ if (msg->hdr.reply_code)
+ return convert_error(zq, reply);
+ if (reply->len < sizeof(struct type86x_reply))
+ return -EINVAL;
if (msg->cprbx.ccp_rtcode &&
msg->cprbx.ccp_rscode == 0x14f &&
outputdatalength > 256) {
@@ -792,8 +807,6 @@ static int convert_response_ica(struct zcrypt_queue *zq,
return -EINVAL;
}
}
- if (msg->hdr.reply_code)
- return convert_error(zq, reply);
if (msg->cprbx.cprb_ver_id == 0x02)
return convert_type86_ica(zq, reply,
outputdata, outputdatalength);
@@ -820,6 +833,8 @@ static int convert_response_xcrb(bool userspace, struct zcrypt_queue *zq,
{
struct type86x_reply *msg = reply->msg;
+ /* reply->len is always >= sizeof(struct error_hdr) here */
+
switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
@@ -827,9 +842,16 @@ static int convert_response_xcrb(bool userspace, struct zcrypt_queue *zq,
return convert_error(zq, reply);
case TYPE86_RSP_CODE:
if (msg->hdr.reply_code) {
- xcrb->status = msg->fmt2.apfs;
+ if (reply->len < sizeof(struct type86_fmt2_msg))
+ xcrb->status = 0x0008044DL;
+ else
+ xcrb->status = msg->fmt2.apfs;
return convert_error(zq, reply);
}
+ if (reply->len < sizeof(struct type86_fmt2_msg) +
+ offsetof(struct CPRBX, cprb_ver_id) +
+ sizeof(msg->cprbx.cprb_ver_id))
+ return -EINVAL;
if (msg->cprbx.cprb_ver_id == 0x02)
return convert_type86_xcrb(userspace, zq, reply, xcrb);
fallthrough; /* wrong cprb version is an unknown response */
@@ -854,6 +876,8 @@ static int convert_response_ep11_xcrb(bool userspace, struct zcrypt_queue *zq,
{
struct type86_ep11_reply *msg = reply->msg;
+ /* reply->len is always >= sizeof(struct error_hdr) here */
+
switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE87_RSP_CODE:
@@ -861,6 +885,8 @@ static int convert_response_ep11_xcrb(bool userspace, struct zcrypt_queue *zq,
case TYPE86_RSP_CODE:
if (msg->hdr.reply_code)
return convert_error(zq, reply);
+ if (reply->len < sizeof(struct type86_ep11_reply))
+ return -EINVAL;
if (msg->cprbx.cprb_ver_id == 0x04)
return convert_type86_ep11_xcrb(userspace, zq, reply, xcrb);
fallthrough; /* wrong cprb version is an unknown resp */
@@ -885,6 +911,8 @@ static int convert_response_rng(struct zcrypt_queue *zq,
{
struct type86x_reply *msg = reply->msg;
+ /* reply->len is always >= sizeof(struct error_hdr) here */
+
switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
@@ -892,6 +920,8 @@ static int convert_response_rng(struct zcrypt_queue *zq,
case TYPE86_RSP_CODE:
if (msg->hdr.reply_code)
return -EINVAL;
+ if (reply->len < sizeof(struct type86x_reply))
+ return -EINVAL;
if (msg->cprbx.cprb_ver_id == 0x02)
return convert_type86_rng(zq, reply, data);
fallthrough; /* wrong cprb version is an unknown response */
@@ -928,48 +958,85 @@ static void zcrypt_msgtype6_receive(struct ap_queue *aq,
};
struct ap_response_type *resp_type = &msg->response;
struct type86x_reply *t86r;
- int len;
+ size_t minlen, len;
/* Copy the reply message to the request message buffer. */
if (!reply)
goto out; /* ap_msg->rc indicates the error */
+
t86r = reply->msg;
- if (t86r->hdr.type == TYPE86_RSP_CODE &&
- t86r->cprbx.cprb_ver_id == 0x02) {
- switch (resp_type->type) {
- case CEXXC_RESPONSE_TYPE_ICA:
- len = sizeof(struct type86x_reply) + t86r->length;
- if (len > reply->bufsize || len > msg->bufsize ||
- len != reply->len) {
- pr_debug("len mismatch => EMSGSIZE\n");
- msg->rc = -EMSGSIZE;
- goto out;
- }
- memcpy(msg->msg, reply->msg, len);
- msg->len = len;
- break;
- case CEXXC_RESPONSE_TYPE_XCRB:
- if (t86r->fmt2.count2)
- len = t86r->fmt2.offset2 + t86r->fmt2.count2;
- else
- len = t86r->fmt2.offset1 + t86r->fmt2.count1;
- if (len > reply->bufsize || len > msg->bufsize ||
- len != reply->len) {
- pr_debug("len mismatch => EMSGSIZE\n");
+ minlen = sizeof(t86r->hdr) + sizeof(t86r->fmt2) +
+ offsetof(struct CPRBX, cprb_ver_id) +
+ sizeof(t86r->cprbx.cprb_ver_id);
+
+ if (reply->len < minlen ||
+ t86r->hdr.type != TYPE86_RSP_CODE ||
+ t86r->cprbx.cprb_ver_id != 0x02) {
+ if (reply->len < sizeof(error_reply)) {
+ /* total broken reply, use static error reply instead */
+ memcpy(msg->msg, &error_reply, sizeof(error_reply));
+ msg->len = sizeof(error_reply);
+ goto out;
+ } else {
+ /* malformed reply, convert function will handle this */
+ len = reply->len;
+ goto copy_len_and_out;
+ }
+ }
+
+ switch (resp_type->type) {
+ case CEXXC_RESPONSE_TYPE_ICA:
+ if (reply->len < sizeof(struct type86x_reply)) {
+ msg->rc = -EMSGSIZE;
+ pr_debug("rpl.len %zu < struct type86_reply, msg.rc=%d\n",
+ reply->len, msg->rc);
+ goto out;
+ }
+ len = sizeof(struct type86x_reply) + (size_t)t86r->length;
+ break;
+ case CEXXC_RESPONSE_TYPE_XCRB:
+ len = (size_t)t86r->fmt2.offset1 + (size_t)t86r->fmt2.count1;
+ if (len > reply->len) {
+ msg->rc = -EMSGSIZE;
+ pr_debug("offset1 %u count1 %u rpl.len %zu mismatch, msg.rc=%d\n",
+ t86r->fmt2.offset1, t86r->fmt2.count1,
+ reply->len, msg->rc);
+ goto out;
+ }
+ if (t86r->fmt2.count2) {
+ len = (size_t)t86r->fmt2.offset2 +
+ (size_t)t86r->fmt2.count2;
+ if (len > reply->len) {
msg->rc = -EMSGSIZE;
+ pr_debug("offset2 %u count2 %u rpl.len %zu mismatch, msg.rc=%d\n",
+ t86r->fmt2.offset2, t86r->fmt2.count2,
+ reply->len, msg->rc);
goto out;
}
- memcpy(msg->msg, reply->msg, len);
- msg->len = len;
- break;
- default:
- memcpy(msg->msg, &error_reply, sizeof(error_reply));
- msg->len = sizeof(error_reply);
}
- } else {
- memcpy(msg->msg, reply->msg, sizeof(error_reply));
+ break;
+ default:
+ memcpy(msg->msg, &error_reply, sizeof(error_reply));
msg->len = sizeof(error_reply);
+ goto out;
+ }
+
+copy_len_and_out:
+ if (len != reply->len) {
+ msg->rc = -EMSGSIZE;
+ pr_debug("len %zu rpl.len %zu mismatch, msg.rc=%d\n",
+ len, reply->len, msg->rc);
+ goto out;
+ }
+ if (len > reply->bufsize || len > msg->bufsize) {
+ msg->rc = -EMSGSIZE;
+ pr_debug("len %zu exceeds buf %zu/%zu, msg.rc=%d\n",
+ len, reply->bufsize, msg->bufsize, msg->rc);
+ goto out;
}
+ memcpy(msg->msg, reply->msg, len);
+ msg->len = len;
+
out:
complete(&resp_type->work);
}
@@ -992,34 +1059,65 @@ static void zcrypt_msgtype6_receive_ep11(struct ap_queue *aq,
};
struct ap_response_type *resp_type = &msg->response;
struct type86_ep11_reply *t86r;
- int len;
+ size_t minlen, len;
/* Copy the reply message to the request message buffer. */
if (!reply)
goto out; /* ap_msg->rc indicates the error */
+
t86r = reply->msg;
- if (t86r->hdr.type == TYPE86_RSP_CODE &&
- t86r->cprbx.cprb_ver_id == 0x04) {
- switch (resp_type->type) {
- case CEXXC_RESPONSE_TYPE_EP11:
- len = t86r->fmt2.offset1 + t86r->fmt2.count1;
- if (len > reply->bufsize || len > msg->bufsize ||
- len != reply->len) {
- pr_debug("len mismatch => EMSGSIZE\n");
- msg->rc = -EMSGSIZE;
- goto out;
- }
- memcpy(msg->msg, reply->msg, len);
- msg->len = len;
- break;
- default:
+ minlen = sizeof(t86r->hdr) + sizeof(t86r->fmt2) +
+ offsetof(struct ep11_cprb, cprb_ver_id) +
+ sizeof(t86r->cprbx.cprb_ver_id);
+
+ if (reply->len < minlen ||
+ t86r->hdr.type != TYPE86_RSP_CODE ||
+ t86r->cprbx.cprb_ver_id != 0x04) {
+ if (reply->len < sizeof(error_reply)) {
+ /* total broken reply, use static error reply instead */
memcpy(msg->msg, &error_reply, sizeof(error_reply));
msg->len = sizeof(error_reply);
+ goto out;
+ } else {
+ /* malformed reply, convert function will handle this */
+ len = reply->len;
+ goto copy_len_and_out;
}
- } else {
- memcpy(msg->msg, reply->msg, sizeof(error_reply));
+ }
+
+ switch (resp_type->type) {
+ case CEXXC_RESPONSE_TYPE_EP11:
+ len = (size_t)t86r->fmt2.offset1 + (size_t)t86r->fmt2.count1;
+ if (len > reply->len) {
+ msg->rc = -EMSGSIZE;
+ pr_debug("offset1 %u count1 %u rpl.len %zu mismatch, msg.rc=%d\n",
+ t86r->fmt2.offset1, t86r->fmt2.count1,
+ reply->len, msg->rc);
+ goto out;
+ }
+ break;
+ default:
+ memcpy(msg->msg, &error_reply, sizeof(error_reply));
msg->len = sizeof(error_reply);
+ goto out;
+ }
+
+copy_len_and_out:
+ if (len != reply->len) {
+ msg->rc = -EMSGSIZE;
+ pr_debug("len %zu rpl.len %zu mismatch, msg.rc=%d\n",
+ len, reply->len, msg->rc);
+ goto out;
+ }
+ if (len > reply->bufsize || len > msg->bufsize) {
+ msg->rc = -EMSGSIZE;
+ pr_debug("len %zu exceeds buf %zu/%zu, msg.rc=%d\n",
+ len, reply->bufsize, msg->bufsize, msg->rc);
+ goto out;
}
+ memcpy(msg->msg, reply->msg, len);
+ msg->len = len;
+
out:
complete(&resp_type->work);
}