* [PATCH v6 1/5] s390/sclp: Make parsing of received event buffers more robust
2026-09-22 6:42 [PATCH v6 0/5] s390/sclp: Misc fixes Alexander Egorenkov
@ 2026-09-22 6:42 ` Alexander Egorenkov
2026-09-22 6:51 ` sashiko-bot
2026-09-22 6:43 ` [PATCH v6 2/5] s390/sclp: Introduce macro sclp_gds_for_each() Alexander Egorenkov
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Alexander Egorenkov @ 2026-09-22 6:42 UTC (permalink / raw)
To: oberpar; +Cc: gor, hca, agordeev, borntraeger, linux390-list, linux-s390
Ensure that
* an event buffer does not cross the end boundary of the SCCB buffer it is
part of
* the total length of an event buffer is not smaller than the size of
its header
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
---
drivers/s390/char/sclp.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 98e334724a62..7be4acec9d6e 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -532,8 +532,7 @@ EXPORT_SYMBOL(sclp_add_request);
/* Dispatch events found in request buffer to registered listeners. Return 0
* if all events were dispatched, non-zero otherwise. */
-static int
-sclp_dispatch_evbufs(struct sccb_header *sccb)
+static int sclp_dispatch_evbufs(struct sccb_header *sccb)
{
unsigned long flags;
struct evbuf_header *evbuf;
@@ -544,11 +543,13 @@ sclp_dispatch_evbufs(struct sccb_header *sccb)
spin_lock_irqsave(&sclp_lock, flags);
rc = 0;
- for (offset = sizeof(struct sccb_header); offset < sccb->length;
+ for (offset = sizeof(struct sccb_header);
+ offset < sccb->length && (sccb->length - offset >= sizeof(struct evbuf_header));
offset += evbuf->length) {
evbuf = (struct evbuf_header *) ((addr_t) sccb + offset);
/* Check for malformed hardware response */
- if (evbuf->length == 0)
+ if (evbuf->length < sizeof(*evbuf) ||
+ offset + evbuf->length > sccb->length)
break;
/* Search for event handler */
reg = NULL;
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v6 2/5] s390/sclp: Introduce macro sclp_gds_for_each()
2026-09-22 6:42 [PATCH v6 0/5] s390/sclp: Misc fixes Alexander Egorenkov
2026-09-22 6:42 ` [PATCH v6 1/5] s390/sclp: Make parsing of received event buffers more robust Alexander Egorenkov
@ 2026-09-22 6:43 ` Alexander Egorenkov
2026-09-22 6:54 ` sashiko-bot
2026-09-22 6:43 ` [PATCH v6 3/5] s390/sclp_tty: Make use of sclp_gds_for_each() Alexander Egorenkov
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Alexander Egorenkov @ 2026-09-22 6:43 UTC (permalink / raw)
To: oberpar; +Cc: gor, hca, agordeev, borntraeger, linux390-list, linux-s390
sclp_find_gds_{sub}vector() does not deal well with malformed event buffers
consisting of GDS {sub}vectors. This can result in an infinite loop or
an out-of-bounds memory read. Therefore, abort with NULL if
* the next GDS header would exceed the given end boundary
* the length in a GDS header contains an invalid value.
A valid length value in a GDS header should be at least as large
as the size of the corresponding GDS header (2 or 4 bytes)
but also not lead to exceeding the given end boundary.
Use the new macro in sclp_find_gds_{sub}vector() to iterate over entries
of a GDS {sub}vector in a safe manner bailing out on the first invalid
entry.
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Suggested-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Fixes: 30c2df51173e ("[S390] sclp: event buffer dissection")
---
drivers/s390/char/sclp.h | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index b31a680e0871..a006d7abfdf3 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -360,25 +360,33 @@ sclp_ascebc_str(char *str, int nr)
(machine_is_vm()) ? ASCEBC(str, nr) : ASCEBC_500(str, nr);
}
-static inline struct gds_vector *
-sclp_find_gds_vector(void *start, void *end, u16 id)
+/* Loop over all GDS {sub}vectors in a safe manner. */
+#define sclp_gds_for_each(v, n, start, end) \
+ for ((n) = (void*)(end) - (void*)(start), (v) = (typeof(v))(start); \
+ (n) >= sizeof(*(v)) && (v)->length >= sizeof(*(v)) && (v)->length <= (n); \
+ (n) -= (v)->length, (v) = (void*)(v) + (v)->length)
+
+static inline struct gds_vector *sclp_find_gds_vector(void *start,
+ void *end, u16 id)
{
struct gds_vector *v;
-
- for (v = start; (void *) v < end; v = (void *) v + v->length)
+ int n;
+ sclp_gds_for_each(v, n, start, end) {
if (v->gds_id == id)
return v;
+ }
return NULL;
}
-static inline struct gds_subvector *
-sclp_find_gds_subvector(void *start, void *end, u8 key)
+static inline struct gds_subvector *sclp_find_gds_subvector(void *start,
+ void *end, u8 key)
{
struct gds_subvector *sv;
-
- for (sv = start; (void *) sv < end; sv = (void *) sv + sv->length)
+ int n;
+ sclp_gds_for_each(sv, n, start, end) {
if (sv->key == key)
return sv;
+ }
return NULL;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v6 2/5] s390/sclp: Introduce macro sclp_gds_for_each()
2026-09-22 6:43 ` [PATCH v6 2/5] s390/sclp: Introduce macro sclp_gds_for_each() Alexander Egorenkov
@ 2026-09-22 6:54 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-09-22 6:54 UTC (permalink / raw)
To: Alexander Egorenkov
Cc: Vasily Gorbik, linux-s390, Heiko Carstens, Alexander Gordeev,
Christian Borntraeger
> sclp_find_gds_{sub}vector() does not deal well with malformed event buffers
> consisting of GDS {sub}vectors. This can result in an infinite loop or
> an out-of-bounds memory read. Therefore, abort with NULL if
> * the next GDS header would exceed the given end boundary
> * the length in a GDS header contains an invalid value.
> A valid length value in a GDS header should be at least as large
> as the size of the corresponding GDS header (2 or 4 bytes)
> but also not lead to exceeding the given end boundary.
>
> Use the new macro in sclp_find_gds_{sub}vector() to iterate over entries
> of a GDS {sub}vector in a safe manner bailing out on the first invalid
> entry.
>
> Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260922064303.524293-1-egorenar@linux.ibm.com?part=2
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v6 3/5] s390/sclp_tty: Make use of sclp_gds_for_each()
2026-09-22 6:42 [PATCH v6 0/5] s390/sclp: Misc fixes Alexander Egorenkov
2026-09-22 6:42 ` [PATCH v6 1/5] s390/sclp: Make parsing of received event buffers more robust Alexander Egorenkov
2026-09-22 6:43 ` [PATCH v6 2/5] s390/sclp: Introduce macro sclp_gds_for_each() Alexander Egorenkov
@ 2026-09-22 6:43 ` Alexander Egorenkov
2026-09-22 6:52 ` sashiko-bot
2026-09-22 6:43 ` [PATCH v6 4/5] s390/sclp_ocf: Fix computation of length of GDS values Alexander Egorenkov
2026-09-22 6:43 ` [PATCH v6 5/5] s390/sclp: Ensure no callback gets called after sclp_unregister() returns Alexander Egorenkov
4 siblings, 1 reply; 11+ messages in thread
From: Alexander Egorenkov @ 2026-09-22 6:43 UTC (permalink / raw)
To: oberpar; +Cc: gor, hca, agordeev, borntraeger, linux390-list, linux-s390
Replace manual and error-prone iteration over entries of a GDS {sub}vector
with sclp_gds_for_each() which deals with erroneous entries in GDS
{sub}vectors more safely.
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Suggested-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Fixes: 30c2df51173e ("[S390] sclp: event buffer dissection")
---
drivers/s390/char/sclp_tty.c | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index 0a92d08830e7..95ab3b090081 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -418,38 +418,32 @@ static void sclp_get_input(struct gds_subvector *sv)
static inline void sclp_eval_selfdeftextmsg(struct gds_subvector *sv)
{
- void *end;
-
- end = (void *) sv + sv->length;
- for (sv = sv + 1; (void *) sv < end; sv = (void *) sv + sv->length)
+ int n;
+ sclp_gds_for_each(sv, n, sv + 1, (void *)sv + sv->length) {
if (sv->key == 0x30)
sclp_get_input(sv);
+ }
}
static inline void sclp_eval_textcmd(struct gds_vector *v)
{
struct gds_subvector *sv;
- void *end;
-
- end = (void *) v + v->length;
- for (sv = (struct gds_subvector *) (v + 1);
- (void *) sv < end; sv = (void *) sv + sv->length)
+ int n;
+ sclp_gds_for_each(sv, n, v + 1, (void *)v + v->length) {
if (sv->key == GDS_KEY_SELFDEFTEXTMSG)
sclp_eval_selfdeftextmsg(sv);
-
+ }
}
static inline void sclp_eval_cpmsu(struct gds_vector *v)
{
- void *end;
-
- end = (void *) v + v->length;
- for (v = v + 1; (void *) v < end; v = (void *) v + v->length)
+ int n;
+ sclp_gds_for_each(v, n, v + 1, (void *)v + v->length) {
if (v->gds_id == GDS_ID_TEXTCMD)
sclp_eval_textcmd(v);
+ }
}
-
static inline void sclp_eval_mdsmu(struct gds_vector *v)
{
v = sclp_find_gds_vector(v + 1, (void *) v + v->length, GDS_ID_CPMSU);
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v6 4/5] s390/sclp_ocf: Fix computation of length of GDS values
2026-09-22 6:42 [PATCH v6 0/5] s390/sclp: Misc fixes Alexander Egorenkov
` (2 preceding siblings ...)
2026-09-22 6:43 ` [PATCH v6 3/5] s390/sclp_tty: Make use of sclp_gds_for_each() Alexander Egorenkov
@ 2026-09-22 6:43 ` Alexander Egorenkov
2026-09-22 6:54 ` sashiko-bot
2026-09-22 6:43 ` [PATCH v6 5/5] s390/sclp: Ensure no callback gets called after sclp_unregister() returns Alexander Egorenkov
4 siblings, 1 reply; 11+ messages in thread
From: Alexander Egorenkov @ 2026-09-22 6:43 UTC (permalink / raw)
To: oberpar; +Cc: gor, hca, agordeev, borntraeger, linux390-list, linux-s390
Fix length calculation for HMC-network and CPC names in OCF event data
by excluding the GDS subvector header size. This prevents a potential
invalid read access beyond the available GDS name data.
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Fixes: 7eb9d5bec552 ("[S390] get CPC image name")
---
drivers/s390/char/sclp_ocf.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/s390/char/sclp_ocf.c b/drivers/s390/char/sclp_ocf.c
index 35f3a4a08b12..cee4bfa4a48a 100644
--- a/drivers/s390/char/sclp_ocf.c
+++ b/drivers/s390/char/sclp_ocf.c
@@ -66,13 +66,13 @@ static void sclp_ocf_handler(struct evbuf_header *evbuf)
/* Copy network name and cpc name. */
spin_lock(&sclp_ocf_lock);
if (netid) {
- size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length);
+ size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length - sizeof(*netid));
memcpy(hmc_network, netid + 1, size);
EBCASC(hmc_network, size);
hmc_network[size] = 0;
}
if (cpc) {
- size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length);
+ size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length - sizeof(*cpc));
memset(cpc_name, 0, OCF_LENGTH_CPC_NAME);
memcpy(cpc_name, cpc + 1, size);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v6 5/5] s390/sclp: Ensure no callback gets called after sclp_unregister() returns
2026-09-22 6:42 [PATCH v6 0/5] s390/sclp: Misc fixes Alexander Egorenkov
` (3 preceding siblings ...)
2026-09-22 6:43 ` [PATCH v6 4/5] s390/sclp_ocf: Fix computation of length of GDS values Alexander Egorenkov
@ 2026-09-22 6:43 ` Alexander Egorenkov
2026-09-22 6:52 ` sashiko-bot
4 siblings, 1 reply; 11+ messages in thread
From: Alexander Egorenkov @ 2026-09-22 6:43 UTC (permalink / raw)
To: oberpar; +Cc: gor, hca, agordeev, borntraeger, linux390-list, linux-s390
There is a potential race condition between sclp_unregister()
and sclp_dispatch_evbufs()/sclp_dispatch_state_change(). As a result,
it is not guaranteed that the callbacks registered with sclp_register()
will not get called one more time (and no more than one) after
sclp_unregister() returns.
The basic idea is to use a single global wait queue which is woken up
when no SCLP WRITE_EVENT_MASK and no SCLP READ_EVENT_DATA request is
outstanding. If both are true then it is guaranteed that no sclp_register
callback could be in-flight. Once the struct sclp_register given to
sclp_unregister() is removed from the list sclp_reg_list, no further
callbacks can be scheduled for the given struct sclp_register and it only
remains to wait until the SCLP driver becomes idle.
With this change sclp_unregister() may no longer be invoked from atomic
context or registered callbacks. But this represents no problem because
this is no regular use case and no driver using sclp_unregister() requires
this at the moment and likely should not require it in the future.
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Suggested-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
---
drivers/s390/char/sclp.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 7be4acec9d6e..eccc3f2fb491 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -20,6 +20,7 @@
#include <linux/jiffies.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/wait.h>
#include <asm/types.h>
#include <asm/irq.h>
#include <asm/debug.h>
@@ -67,6 +68,8 @@ static struct sclp_req sclp_init_req;
static void *sclp_read_sccb;
static struct init_sccb *sclp_init_sccb;
+static DECLARE_WAIT_QUEUE_HEAD(sclp_state_wq);
+
/* Number of console pages to allocate, used by sclp_con.c and sclp_vt220.c */
int sclp_console_pages = SCLP_CONSOLE_PAGES;
/* Flag to indicate if buffer pages are dropped on buffer full condition */
@@ -589,6 +592,7 @@ sclp_read_cb(struct sclp_req *req, void *data)
sclp_dispatch_evbufs(sccb);
spin_lock_irqsave(&sclp_lock, flags);
sclp_reading_state = sclp_reading_state_idle;
+ wake_up_all(&sclp_state_wq);
spin_unlock_irqrestore(&sclp_lock, flags);
}
@@ -899,19 +903,26 @@ sclp_register(struct sclp_register *reg)
EXPORT_SYMBOL(sclp_register);
-/* Unregister event listener. */
-void
-sclp_unregister(struct sclp_register *reg)
+/* Unregister event listener.
+ * This function may sleep. Do not call it from atomic context or
+ * sclp_register.receive_fn(). */
+void sclp_unregister(struct sclp_register *reg)
{
- unsigned long flags;
+ might_sleep();
/* UREG: Event listener unregistered (b=caller) */
sclp_trace_register(2, "UREG", 0, _RET_IP_, reg);
- spin_lock_irqsave(&sclp_lock, flags);
+ spin_lock_irq(&sclp_lock);
list_del(®->list);
- spin_unlock_irqrestore(&sclp_lock, flags);
+ spin_unlock_irq(&sclp_lock);
sclp_init_mask(1);
+ spin_lock_irq(&sclp_lock);
+ wait_event_lock_irq(sclp_state_wq,
+ (sclp_mask_state == sclp_mask_state_idle) &&
+ (sclp_reading_state == sclp_reading_state_idle),
+ sclp_lock);
+ spin_unlock_irq(&sclp_lock);
}
EXPORT_SYMBOL(sclp_unregister);
@@ -1037,6 +1048,7 @@ sclp_init_mask(int calculate)
}
}
sclp_mask_state = sclp_mask_state_idle;
+ wake_up_all(&sclp_state_wq);
spin_unlock_irqrestore(&sclp_lock, flags);
return rc;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v6 5/5] s390/sclp: Ensure no callback gets called after sclp_unregister() returns
2026-09-22 6:43 ` [PATCH v6 5/5] s390/sclp: Ensure no callback gets called after sclp_unregister() returns Alexander Egorenkov
@ 2026-09-22 6:52 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-09-22 6:52 UTC (permalink / raw)
To: Alexander Egorenkov
Cc: Alexander Gordeev, Christian Borntraeger, Vasily Gorbik,
linux-s390, Heiko Carstens
> There is a potential race condition between sclp_unregister()
> and sclp_dispatch_evbufs()/sclp_dispatch_state_change(). As a result,
> it is not guaranteed that the callbacks registered with sclp_register()
> will not get called one more time (and no more than one) after
> sclp_unregister() returns.
>
> The basic idea is to use a single global wait queue which is woken up
> when no SCLP WRITE_EVENT_MASK and no SCLP READ_EVENT_DATA request is
> outstanding. If both are true then it is guaranteed that no sclp_register
> callback could be in-flight. Once the struct sclp_register given to
> sclp_unregister() is removed from the list sclp_reg_list, no further
> callbacks can be scheduled for the given struct sclp_register and it only
> remains to wait until the SCLP driver becomes idle.
>
> With this change sclp_unregister() may no longer be invoked from atomic
> context or registered callbacks. But this represents no problem because
> this is no regular use case and no driver using sclp_unregister() requires
> this at the moment and likely should not require it in the future.
>
> Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260922064303.524293-1-egorenar@linux.ibm.com?part=5
^ permalink raw reply [flat|nested] 11+ messages in thread