* [PATCH 1/6] platform/wmi: Add wmidev_invoke_procedure()
2026-04-06 20:32 [PATCH 0/6] platform/wmi: Use WMI driver core for size checks Armin Wolf
@ 2026-04-06 20:32 ` Armin Wolf
2026-04-06 20:32 ` [PATCH 2/6] platform/wmi: Convert drivers to use wmidev_invoke_procedure() Armin Wolf
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Armin Wolf @ 2026-04-06 20:32 UTC (permalink / raw)
To: hansg; +Cc: ilpo.jarvinen, platform-driver-x86, linux-kernel
Some WMI methods return no values, so the whole postprocessing
of the result data is not needed for them. Add a special function
for calling such WMI methods to prepare for future changes of
the main wmidev_invoke_method() function.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
.../wmi/driver-development-guide.rst | 3 +-
drivers/platform/wmi/core.c | 44 +++++++++++++++++++
include/linux/wmi.h | 3 ++
3 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/Documentation/wmi/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst
index fbc2d9b12fe9..5b94402874c4 100644
--- a/Documentation/wmi/driver-development-guide.rst
+++ b/Documentation/wmi/driver-development-guide.rst
@@ -106,7 +106,8 @@ WMI method drivers
WMI drivers can call WMI device methods using wmidev_invoke_method(). For each WMI method
invocation the WMI driver needs to provide the instance number and the method ID, as well as
-a buffer with the method arguments and optionally a buffer for the results.
+a buffer with the method arguments and optionally a buffer for the results. When calling WMI
+methods that do not return any values, wmidev_invoke_procedure() should be used instead.
The layout of said buffers is device-specific and described by the Binary MOF data associated
with a given WMI device. Said Binary MOF data also describes the method ID of a given WMI method
diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c
index b8e6b9a421c6..7cc5ca11a60d 100644
--- a/drivers/platform/wmi/core.c
+++ b/drivers/platform/wmi/core.c
@@ -427,6 +427,50 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
}
EXPORT_SYMBOL_GPL(wmidev_invoke_method);
+/**
+ * wmidev_invoke_procedure - Invoke a WMI method that does not return values
+ * @wdev: A wmi bus device from a driver
+ * @instance: Instance index
+ * @method_id: Method ID to call
+ * @in: Mandatory WMI buffer containing input for the method call
+ *
+ * Invoke a WMI method that does not return any values. Use wmidev_invoke_method()
+ * for WMI methods that do return values.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id,
+ const struct wmi_buffer *in)
+{
+ struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
+ struct acpi_buffer ain;
+ acpi_status status;
+ int ret;
+
+ if (wblock->gblock.flags & ACPI_WMI_STRING) {
+ ret = wmi_marshal_string(in, &ain);
+ if (ret < 0)
+ return ret;
+ } else {
+ if (in->length > U32_MAX)
+ return -E2BIG;
+
+ ain.length = in->length;
+ ain.pointer = in->data;
+ }
+
+ status = wmidev_evaluate_method(wdev, instance, method_id, &ain, NULL);
+
+ if (wblock->gblock.flags & ACPI_WMI_STRING)
+ kfree(ain.pointer);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wmidev_invoke_procedure);
+
static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
struct acpi_buffer *out)
{
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index 75cb0c7cfe57..b00950dc1231 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -70,6 +70,9 @@ ssize_t wmi_string_from_utf8s(struct wmi_string *str, size_t max_chars, const u8
int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
const struct wmi_buffer *in, struct wmi_buffer *out);
+int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id,
+ const struct wmi_buffer *in);
+
int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out);
int wmidev_set_block(struct wmi_device *wdev, u8 instance, const struct wmi_buffer *in);
--
2.39.5
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 2/6] platform/wmi: Convert drivers to use wmidev_invoke_procedure()
2026-04-06 20:32 [PATCH 0/6] platform/wmi: Use WMI driver core for size checks Armin Wolf
2026-04-06 20:32 ` [PATCH 1/6] platform/wmi: Add wmidev_invoke_procedure() Armin Wolf
@ 2026-04-06 20:32 ` Armin Wolf
2026-04-06 20:32 ` [PATCH 3/6] platform/wmi: Prepare to reject undersized unmarshalling results Armin Wolf
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Armin Wolf @ 2026-04-06 20:32 UTC (permalink / raw)
To: hansg; +Cc: ilpo.jarvinen, platform-driver-x86, linux-kernel
Convert users of wmidev_invoke_method() to wmidev_invoke_procedure()
where applicable to prepare for future changes.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
drivers/platform/x86/bitland-mifs-wmi.c | 17 +++++++++--------
drivers/platform/x86/intel/wmi/thunderbolt.c | 2 +-
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 54380708b7b0..cd3cdd087511 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -167,23 +167,24 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
struct bitland_mifs_output *output)
{
struct wmi_buffer in_buf = { .length = sizeof(*input), .data = (void *)input };
+ void *out_data __free(kfree) = NULL;
struct wmi_buffer out_buf = { 0 };
int ret;
guard(mutex)(&data->lock);
- ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, output ? &out_buf : NULL);
+ if (!output)
+ return wmidev_invoke_procedure(data->wdev, 0, 1, &in_buf);
+
+ ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, &out_buf);
if (ret)
return ret;
- if (output) {
- void *out_data __free(kfree) = out_buf.data;
-
- if (out_buf.length < sizeof(*output))
- return -EIO;
+ out_data = out_buf.data;
+ if (out_buf.length < sizeof(*output))
+ return -EIO;
- memcpy(output, out_data, sizeof(*output));
- }
+ memcpy(output, out_data, sizeof(*output));
return 0;
}
diff --git a/drivers/platform/x86/intel/wmi/thunderbolt.c b/drivers/platform/x86/intel/wmi/thunderbolt.c
index 47017f2d7597..9b1920d61674 100644
--- a/drivers/platform/x86/intel/wmi/thunderbolt.c
+++ b/drivers/platform/x86/intel/wmi/thunderbolt.c
@@ -34,7 +34,7 @@ static ssize_t force_power_store(struct device *dev,
if (mode > 1)
return -EINVAL;
- ret = wmidev_invoke_method(to_wmi_device(dev), 0, 1, &buffer, NULL);
+ ret = wmidev_invoke_procedure(to_wmi_device(dev), 0, 1, &buffer);
if (ret < 0)
return ret;
--
2.39.5
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 3/6] platform/wmi: Prepare to reject undersized unmarshalling results
2026-04-06 20:32 [PATCH 0/6] platform/wmi: Use WMI driver core for size checks Armin Wolf
2026-04-06 20:32 ` [PATCH 1/6] platform/wmi: Add wmidev_invoke_procedure() Armin Wolf
2026-04-06 20:32 ` [PATCH 2/6] platform/wmi: Convert drivers to use wmidev_invoke_procedure() Armin Wolf
@ 2026-04-06 20:32 ` Armin Wolf
2026-04-06 20:32 ` [PATCH 4/6] platform/wmi: Extend wmidev_invoke_method() to reject undersized data Armin Wolf
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Armin Wolf @ 2026-04-06 20:32 UTC (permalink / raw)
To: hansg; +Cc: ilpo.jarvinen, platform-driver-x86, linux-kernel
Driver using the buffer-based WMI API usually reject buffers resulting
from WMI method calls or block queries if they contain not enough data.
Prepare the WMI core for assisting in this by automatically rejecting
undersized unmarshalling results.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
drivers/platform/wmi/core.c | 6 ++---
drivers/platform/wmi/internal.h | 3 ++-
drivers/platform/wmi/marshalling.c | 6 ++++-
.../platform/wmi/tests/marshalling_kunit.c | 24 +++++++++++++++++--
4 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c
index 7cc5ca11a60d..66ec885bffd7 100644
--- a/drivers/platform/wmi/core.c
+++ b/drivers/platform/wmi/core.c
@@ -420,7 +420,7 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
return 0;
}
- ret = wmi_unmarshal_acpi_object(obj, out);
+ ret = wmi_unmarshal_acpi_object(obj, out, 0);
kfree(obj);
return ret;
@@ -583,7 +583,7 @@ int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *
if (!obj)
return -EIO;
- ret = wmi_unmarshal_acpi_object(obj, out);
+ ret = wmi_unmarshal_acpi_object(obj, out, 0);
kfree(obj);
return ret;
@@ -1416,7 +1416,7 @@ static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)
return;
}
- ret = wmi_unmarshal_acpi_object(obj, &buffer);
+ ret = wmi_unmarshal_acpi_object(obj, &buffer, 0);
if (ret < 0) {
dev_warn(&wblock->dev.dev, "Failed to unmarshal event data: %d\n", ret);
return;
diff --git a/drivers/platform/wmi/internal.h b/drivers/platform/wmi/internal.h
index 9a39ffa31ad1..c02908694563 100644
--- a/drivers/platform/wmi/internal.h
+++ b/drivers/platform/wmi/internal.h
@@ -11,7 +11,8 @@
union acpi_object;
struct wmi_buffer;
-int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer);
+int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer,
+ size_t min_size);
int wmi_marshal_string(const struct wmi_buffer *buffer, struct acpi_buffer *out);
#endif /* _WMI_INTERNAL_H_ */
diff --git a/drivers/platform/wmi/marshalling.c b/drivers/platform/wmi/marshalling.c
index 63a92c4ebab5..87091832568e 100644
--- a/drivers/platform/wmi/marshalling.c
+++ b/drivers/platform/wmi/marshalling.c
@@ -151,7 +151,8 @@ static int wmi_obj_transform(const union acpi_object *obj, u8 *buffer)
return 0;
}
-int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer)
+int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer,
+ size_t min_size)
{
size_t length, alloc_length;
u8 *data;
@@ -161,6 +162,9 @@ int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *b
if (ret < 0)
return ret;
+ if (length < min_size)
+ return -ENODATA;
+
if (ARCH_KMALLOC_MINALIGN < 8) {
/*
* kmalloc() guarantees that the alignment of the resulting memory allocation is at
diff --git a/drivers/platform/wmi/tests/marshalling_kunit.c b/drivers/platform/wmi/tests/marshalling_kunit.c
index 0c7cd8774aa3..471963076d58 100644
--- a/drivers/platform/wmi/tests/marshalling_kunit.c
+++ b/drivers/platform/wmi/tests/marshalling_kunit.c
@@ -372,7 +372,7 @@ static void wmi_unmarshal_acpi_object_test(struct kunit *test)
struct wmi_buffer result;
int ret;
- ret = wmi_unmarshal_acpi_object(¶m->obj, &result);
+ ret = wmi_unmarshal_acpi_object(¶m->obj, &result, param->buffer.length);
if (ret < 0)
KUNIT_FAIL_AND_ABORT(test, "Unmarshalling of ACPI object failed\n");
@@ -389,7 +389,7 @@ static void wmi_unmarshal_acpi_object_failure_test(struct kunit *test)
struct wmi_buffer result;
int ret;
- ret = wmi_unmarshal_acpi_object(¶m->obj, &result);
+ ret = wmi_unmarshal_acpi_object(¶m->obj, &result, 0);
if (ret < 0)
return;
@@ -427,6 +427,25 @@ static void wmi_marshal_string_failure_test(struct kunit *test)
KUNIT_FAIL(test, "Invalid string was not rejected\n");
}
+static void wmi_unmarshal_acpi_object_undersized_test(struct kunit *test)
+{
+ const union acpi_object obj = {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = 0xdeadbeef,
+ },
+ };
+ struct wmi_buffer result;
+ int ret;
+
+ ret = wmi_unmarshal_acpi_object(&obj, &result, sizeof(expected_single_integer) + 1);
+ if (ret < 0)
+ return;
+
+ kfree(result.data);
+ KUNIT_FAIL(test, "Undersized unmarshalling result was not rejected\n");
+}
+
static struct kunit_case wmi_marshalling_test_cases[] = {
KUNIT_CASE_PARAM(wmi_unmarshal_acpi_object_test,
wmi_unmarshal_acpi_object_gen_params),
@@ -436,6 +455,7 @@ static struct kunit_case wmi_marshalling_test_cases[] = {
wmi_unmarshal_acpi_object_failure_gen_params),
KUNIT_CASE_PARAM(wmi_marshal_string_failure_test,
wmi_marshal_string_failure_gen_params),
+ KUNIT_CASE(wmi_unmarshal_acpi_object_undersized_test),
{}
};
--
2.39.5
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 4/6] platform/wmi: Extend wmidev_invoke_method() to reject undersized data
2026-04-06 20:32 [PATCH 0/6] platform/wmi: Use WMI driver core for size checks Armin Wolf
` (2 preceding siblings ...)
2026-04-06 20:32 ` [PATCH 3/6] platform/wmi: Prepare to reject undersized unmarshalling results Armin Wolf
@ 2026-04-06 20:32 ` Armin Wolf
2026-04-06 20:32 ` [PATCH 5/6] platform/wmi: Extend wmidev_query_block() " Armin Wolf
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Armin Wolf @ 2026-04-06 20:32 UTC (permalink / raw)
To: hansg; +Cc: ilpo.jarvinen, platform-driver-x86, linux-kernel
WMI drivers using the buffer-based WMI API are expected to reject
undersized method return values. Extend wmidev_invoke_method() to
enable the WMI driver core to perform this size check internally.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
drivers/platform/wmi/core.c | 23 ++++++++++-------------
drivers/platform/x86/bitland-mifs-wmi.c | 11 +++--------
include/linux/wmi.h | 2 +-
3 files changed, 14 insertions(+), 22 deletions(-)
diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c
index 66ec885bffd7..a1a612f33233 100644
--- a/drivers/platform/wmi/core.c
+++ b/drivers/platform/wmi/core.c
@@ -364,20 +364,23 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 met
EXPORT_SYMBOL_GPL(wmidev_evaluate_method);
/**
- * wmidev_invoke_method - Invoke a WMI method
+ * wmidev_invoke_method - Invoke a WMI method that returns values
* @wdev: A wmi bus device from a driver
* @instance: Instance index
* @method_id: Method ID to call
* @in: Mandatory WMI buffer containing input for the method call
- * @out: Optional WMI buffer to return the method results
+ * @out: Mandatory WMI buffer to return the method results
+ * @min_size: Minimum size of the method result data in bytes
*
- * Invoke a WMI method, the caller must free the resulting data inside @out.
- * Said data is guaranteed to be aligned on a 8-byte boundary.
+ * Invoke a WMI method that returns values, the caller must free the resulting
+ * data inside @out using kfree(). Said data is guaranteed to be aligned on a
+ * 8-byte boundary. Use wmidev_invoke_procedure() for WMI methods that
+ * return no values.
*
* Return: 0 on success or negative error code on failure.
*/
int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
- const struct wmi_buffer *in, struct wmi_buffer *out)
+ const struct wmi_buffer *in, struct wmi_buffer *out, size_t min_size)
{
struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
struct acpi_buffer aout = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -398,10 +401,7 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
ain.pointer = in->data;
}
- if (out)
- status = wmidev_evaluate_method(wdev, instance, method_id, &ain, &aout);
- else
- status = wmidev_evaluate_method(wdev, instance, method_id, &ain, NULL);
+ status = wmidev_evaluate_method(wdev, instance, method_id, &ain, &aout);
if (wblock->gblock.flags & ACPI_WMI_STRING)
kfree(ain.pointer);
@@ -409,9 +409,6 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
if (ACPI_FAILURE(status))
return -EIO;
- if (!out)
- return 0;
-
obj = aout.pointer;
if (!obj) {
out->length = 0;
@@ -420,7 +417,7 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
return 0;
}
- ret = wmi_unmarshal_acpi_object(obj, out, 0);
+ ret = wmi_unmarshal_acpi_object(obj, out, min_size);
kfree(obj);
return ret;
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index cd3cdd087511..78639407d67e 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -10,7 +10,6 @@
#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/bits.h>
-#include <linux/cleanup.h>
#include <linux/container_of.h>
#include <linux/dev_printk.h>
#include <linux/device.h>
@@ -167,7 +166,6 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
struct bitland_mifs_output *output)
{
struct wmi_buffer in_buf = { .length = sizeof(*input), .data = (void *)input };
- void *out_data __free(kfree) = NULL;
struct wmi_buffer out_buf = { 0 };
int ret;
@@ -176,15 +174,12 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data,
if (!output)
return wmidev_invoke_procedure(data->wdev, 0, 1, &in_buf);
- ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, &out_buf);
+ ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, &out_buf, sizeof(*output));
if (ret)
return ret;
- out_data = out_buf.data;
- if (out_buf.length < sizeof(*output))
- return -EIO;
-
- memcpy(output, out_data, sizeof(*output));
+ memcpy(output, out_buf.data, sizeof(*output));
+ kfree(out_buf.data);
return 0;
}
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index b00950dc1231..858398beb01a 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -68,7 +68,7 @@ ssize_t wmi_string_from_utf8s(struct wmi_string *str, size_t max_chars, const u8
size_t src_length);
int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
- const struct wmi_buffer *in, struct wmi_buffer *out);
+ const struct wmi_buffer *in, struct wmi_buffer *out, size_t min_size);
int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id,
const struct wmi_buffer *in);
--
2.39.5
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 5/6] platform/wmi: Extend wmidev_query_block() to reject undersized data
2026-04-06 20:32 [PATCH 0/6] platform/wmi: Use WMI driver core for size checks Armin Wolf
` (3 preceding siblings ...)
2026-04-06 20:32 ` [PATCH 4/6] platform/wmi: Extend wmidev_invoke_method() to reject undersized data Armin Wolf
@ 2026-04-06 20:32 ` Armin Wolf
2026-04-06 20:32 ` [PATCH 6/6] platform/wmi: Replace .no_notify_data with .min_event_size Armin Wolf
2026-04-10 16:28 ` [PATCH 0/6] platform/wmi: Use WMI driver core for size checks Ilpo Järvinen
6 siblings, 0 replies; 9+ messages in thread
From: Armin Wolf @ 2026-04-06 20:32 UTC (permalink / raw)
To: hansg; +Cc: ilpo.jarvinen, platform-driver-x86, linux-kernel
WMI drivers using the buffer-based WMI API are expected to reject
undersized query results. Extend wmidev_query_block() to enable
the WMI driver core to perform this size check internally.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
drivers/platform/wmi/core.c | 10 ++++++----
drivers/platform/x86/intel/wmi/sbl-fw-update.c | 7 +------
drivers/platform/x86/wmi-bmof.c | 2 +-
include/linux/wmi.h | 3 ++-
4 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c
index a1a612f33233..87b0e54dde5a 100644
--- a/drivers/platform/wmi/core.c
+++ b/drivers/platform/wmi/core.c
@@ -565,13 +565,15 @@ EXPORT_SYMBOL_GPL(wmidev_block_query);
* @wdev: A wmi bus device from a driver
* @instance: Instance index
* @out: WMI buffer to fill
+ * @min_size: Minimum size of the result data in bytes
*
- * Query a WMI data block, the caller must free the resulting data inside @out.
- * Said data is guaranteed to be aligned on a 8-byte boundary.
+ * Query a WMI data block, the caller must free the resulting data inside @out
+ * using kfree(). Said data is guaranteed to be aligned on a 8-byte boundary.
*
* Return: 0 on success or a negative error code on failure.
*/
-int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out)
+int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out,
+ size_t min_size)
{
union acpi_object *obj;
int ret;
@@ -580,7 +582,7 @@ int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *
if (!obj)
return -EIO;
- ret = wmi_unmarshal_acpi_object(obj, out, 0);
+ ret = wmi_unmarshal_acpi_object(obj, out, min_size);
kfree(obj);
return ret;
diff --git a/drivers/platform/x86/intel/wmi/sbl-fw-update.c b/drivers/platform/x86/intel/wmi/sbl-fw-update.c
index 3716ccaaed6a..62c9c7f1842b 100644
--- a/drivers/platform/x86/intel/wmi/sbl-fw-update.c
+++ b/drivers/platform/x86/intel/wmi/sbl-fw-update.c
@@ -28,15 +28,10 @@ static int get_fwu_request(struct device *dev, u32 *out)
__le32 *result;
int ret;
- ret = wmidev_query_block(to_wmi_device(dev), 0, &buffer);
+ ret = wmidev_query_block(to_wmi_device(dev), 0, &buffer, sizeof(*result));
if (ret < 0)
return ret;
- if (buffer.length < sizeof(*result)) {
- kfree(buffer.data);
- return -ENODATA;
- }
-
result = buffer.data;
*out = le32_to_cpu(*result);
kfree(result);
diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c
index e3a126de421b..6623cf60e4b4 100644
--- a/drivers/platform/x86/wmi-bmof.c
+++ b/drivers/platform/x86/wmi-bmof.c
@@ -62,7 +62,7 @@ static int wmi_bmof_probe(struct wmi_device *wdev, const void *context)
if (!buffer)
return -ENOMEM;
- ret = wmidev_query_block(wdev, 0, buffer);
+ ret = wmidev_query_block(wdev, 0, buffer, 0);
if (ret < 0)
return ret;
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index 858398beb01a..da94580572a9 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -73,7 +73,8 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id,
int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id,
const struct wmi_buffer *in);
-int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out);
+int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out,
+ size_t min_size);
int wmidev_set_block(struct wmi_device *wdev, u8 instance, const struct wmi_buffer *in);
--
2.39.5
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH 6/6] platform/wmi: Replace .no_notify_data with .min_event_size
2026-04-06 20:32 [PATCH 0/6] platform/wmi: Use WMI driver core for size checks Armin Wolf
` (4 preceding siblings ...)
2026-04-06 20:32 ` [PATCH 5/6] platform/wmi: Extend wmidev_query_block() " Armin Wolf
@ 2026-04-06 20:32 ` Armin Wolf
2026-04-10 16:28 ` [PATCH 0/6] platform/wmi: Use WMI driver core for size checks Ilpo Järvinen
6 siblings, 0 replies; 9+ messages in thread
From: Armin Wolf @ 2026-04-06 20:32 UTC (permalink / raw)
To: hansg; +Cc: ilpo.jarvinen, platform-driver-x86, linux-kernel
WMI drivers using the buffer-based WMI API are expected to reject
undersized event payloads. Extend the WMI driver core to allow
such drivers to specify their minimum supported event payload size.
Also remove the now redundant .no_notify_data field.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
---
Documentation/wmi/driver-development-guide.rst | 8 +++++---
drivers/platform/wmi/core.c | 12 ++++++++----
drivers/platform/x86/bitland-mifs-wmi.c | 8 ++------
drivers/platform/x86/dell/dell-wmi-base.c | 1 +
drivers/platform/x86/lenovo/ideapad-laptop.c | 1 +
drivers/platform/x86/lenovo/wmi-camera.c | 1 +
drivers/platform/x86/lenovo/wmi-events.c | 1 +
drivers/platform/x86/lenovo/ymc.c | 1 +
drivers/platform/x86/lenovo/yogabook.c | 2 +-
drivers/platform/x86/redmi-wmi.c | 1 +
drivers/platform/x86/uniwill/uniwill-wmi.c | 1 +
drivers/platform/x86/xiaomi-wmi.c | 1 +
include/linux/wmi.h | 7 +++++--
13 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/Documentation/wmi/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst
index 5b94402874c4..387f508d57ad 100644
--- a/Documentation/wmi/driver-development-guide.rst
+++ b/Documentation/wmi/driver-development-guide.rst
@@ -71,7 +71,7 @@ to matching WMI devices using a struct wmi_device_id table:
.remove = foo_remove, /* optional, devres is preferred */
.shutdown = foo_shutdown, /* optional, called during shutdown */
.notify_new = foo_notify, /* optional, for event handling */
- .no_notify_data = true, /* optional, enables events containing no additional data */
+ .min_event_size = X, /* optional, simplifies event payload size verification */
.no_singleton = true, /* required for new WMI drivers */
};
module_wmi_driver(foo_driver);
@@ -142,8 +142,10 @@ right before and after calling its remove() or shutdown() callback.
However WMI driver developers should be aware that multiple WMI events can be received concurrently,
so any locking (if necessary) needs to be provided by the WMI driver itself.
-In order to be able to receive WMI events containing no additional event data,
-the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``.
+The WMI driver can furthermore instruct the WMI driver core to automatically reject WMI events
+that contain a undersized event payload by populating the ``min_event_size`` field inside
+struct wmi_driver. Setting this field to 0 will thus enable the WMI driver to receive WMI events
+without any event payload.
Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver.
diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c
index 87b0e54dde5a..7df50c8238e5 100644
--- a/drivers/platform/wmi/core.c
+++ b/drivers/platform/wmi/core.c
@@ -1040,7 +1040,7 @@ static int wmi_dev_probe(struct device *dev)
}
if (wdriver->notify || wdriver->notify_new) {
- if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data)
+ if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && wdriver->min_event_size)
return -ENODEV;
}
@@ -1398,10 +1398,14 @@ static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj
static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)
{
struct wmi_driver *driver = to_wmi_driver(wblock->dev.dev.driver);
+ struct wmi_buffer dummy = {
+ .length = 0,
+ .data = ZERO_SIZE_PTR,
+ };
struct wmi_buffer buffer;
int ret;
- if (!obj && !driver->no_notify_data) {
+ if (!obj && driver->min_event_size) {
dev_warn(&wblock->dev.dev, "Event contains no event data\n");
return;
}
@@ -1411,11 +1415,11 @@ static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj)
if (driver->notify_new) {
if (!obj) {
- driver->notify_new(&wblock->dev, NULL);
+ driver->notify_new(&wblock->dev, &dummy);
return;
}
- ret = wmi_unmarshal_acpi_object(obj, &buffer, 0);
+ ret = wmi_unmarshal_acpi_object(obj, &buffer, driver->min_event_size);
if (ret < 0) {
dev_warn(&wblock->dev.dev, "Failed to unmarshal event data: %d\n", ret);
return;
diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c
index 78639407d67e..b0d06a80e89e 100644
--- a/drivers/platform/x86/bitland-mifs-wmi.c
+++ b/drivers/platform/x86/bitland-mifs-wmi.c
@@ -734,15 +734,10 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev,
const struct wmi_buffer *buffer)
{
struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev);
- const struct bitland_mifs_event *event;
+ const struct bitland_mifs_event *event = buffer->data;
struct bitland_fan_notify_data fan_data;
u8 brightness;
- if (buffer->length < sizeof(*event))
- return;
-
- event = buffer->data;
-
/* Validate event type */
if (event->event_type != WMI_EVENT_TYPE_HOTKEY)
return;
@@ -830,6 +825,7 @@ static struct wmi_driver bitland_mifs_wmi_driver = {
.pm = pm_sleep_ptr(&bitland_mifs_wmi_pm_ops),
},
.id_table = bitland_mifs_wmi_id_table,
+ .min_event_size = sizeof(struct bitland_mifs_event),
.probe = bitland_mifs_wmi_probe,
.notify_new = bitland_mifs_wmi_notify,
};
diff --git a/drivers/platform/x86/dell/dell-wmi-base.c b/drivers/platform/x86/dell/dell-wmi-base.c
index e7a411ae9ca1..2a5804efd3ea 100644
--- a/drivers/platform/x86/dell/dell-wmi-base.c
+++ b/drivers/platform/x86/dell/dell-wmi-base.c
@@ -825,6 +825,7 @@ static struct wmi_driver dell_wmi_driver = {
.name = "dell-wmi",
},
.id_table = dell_wmi_id_table,
+ .min_event_size = sizeof(u16),
.probe = dell_wmi_probe,
.remove = dell_wmi_remove,
.notify = dell_wmi_notify,
diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
index ae1ebb071fab..4fbc904f1fc3 100644
--- a/drivers/platform/x86/lenovo/ideapad-laptop.c
+++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
@@ -2340,6 +2340,7 @@ static struct wmi_driver ideapad_wmi_driver = {
.name = "ideapad_wmi",
},
.id_table = ideapad_wmi_ids,
+ .min_event_size = sizeof(u32),
.probe = ideapad_wmi_probe,
.notify = ideapad_wmi_notify,
};
diff --git a/drivers/platform/x86/lenovo/wmi-camera.c b/drivers/platform/x86/lenovo/wmi-camera.c
index eb60fb9a5b3f..89ecbce60bf4 100644
--- a/drivers/platform/x86/lenovo/wmi-camera.c
+++ b/drivers/platform/x86/lenovo/wmi-camera.c
@@ -134,6 +134,7 @@ static struct wmi_driver lenovo_wmi_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = lenovo_wmi_id_table,
+ .min_event_size = sizeof(u8),
.no_singleton = true,
.probe = lenovo_wmi_probe,
.notify = lenovo_wmi_notify,
diff --git a/drivers/platform/x86/lenovo/wmi-events.c b/drivers/platform/x86/lenovo/wmi-events.c
index 0994cd7dd504..4a6a2c82413a 100644
--- a/drivers/platform/x86/lenovo/wmi-events.c
+++ b/drivers/platform/x86/lenovo/wmi-events.c
@@ -183,6 +183,7 @@ static struct wmi_driver lwmi_events_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = lwmi_events_id_table,
+ .min_event_size = sizeof(u32),
.probe = lwmi_events_probe,
.notify = lwmi_events_notify,
.no_singleton = true,
diff --git a/drivers/platform/x86/lenovo/ymc.c b/drivers/platform/x86/lenovo/ymc.c
index 470d53e3c9d2..1b73a55f1b89 100644
--- a/drivers/platform/x86/lenovo/ymc.c
+++ b/drivers/platform/x86/lenovo/ymc.c
@@ -153,6 +153,7 @@ static struct wmi_driver lenovo_ymc_driver = {
.name = "lenovo-ymc",
},
.id_table = lenovo_ymc_wmi_id_table,
+ .min_event_size = sizeof(u32),
.probe = lenovo_ymc_probe,
.notify = lenovo_ymc_notify,
};
diff --git a/drivers/platform/x86/lenovo/yogabook.c b/drivers/platform/x86/lenovo/yogabook.c
index 69887de36c9b..1a4b2ab1f35d 100644
--- a/drivers/platform/x86/lenovo/yogabook.c
+++ b/drivers/platform/x86/lenovo/yogabook.c
@@ -411,8 +411,8 @@ static struct wmi_driver yogabook_wmi_driver = {
.name = "yogabook-wmi",
.pm = pm_sleep_ptr(&yogabook_pm_ops),
},
- .no_notify_data = true,
.id_table = yogabook_wmi_id_table,
+ .min_event_size = 0,
.probe = yogabook_wmi_probe,
.remove = yogabook_wmi_remove,
.notify = yogabook_wmi_notify,
diff --git a/drivers/platform/x86/redmi-wmi.c b/drivers/platform/x86/redmi-wmi.c
index e5cb348e3a39..58898630eda6 100644
--- a/drivers/platform/x86/redmi-wmi.c
+++ b/drivers/platform/x86/redmi-wmi.c
@@ -141,6 +141,7 @@ static struct wmi_driver redmi_wmi_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = redmi_wmi_id_table,
+ .min_event_size = 32,
.probe = redmi_wmi_probe,
.notify = redmi_wmi_notify,
.no_singleton = true,
diff --git a/drivers/platform/x86/uniwill/uniwill-wmi.c b/drivers/platform/x86/uniwill/uniwill-wmi.c
index 31d9c39f14ab..097882f10b1e 100644
--- a/drivers/platform/x86/uniwill/uniwill-wmi.c
+++ b/drivers/platform/x86/uniwill/uniwill-wmi.c
@@ -77,6 +77,7 @@ static struct wmi_driver uniwill_wmi_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = uniwill_wmi_id_table,
+ .min_event_size = sizeof(u32),
.notify = uniwill_wmi_notify,
.no_singleton = true,
};
diff --git a/drivers/platform/x86/xiaomi-wmi.c b/drivers/platform/x86/xiaomi-wmi.c
index badf9e42e015..3874f3336a0d 100644
--- a/drivers/platform/x86/xiaomi-wmi.c
+++ b/drivers/platform/x86/xiaomi-wmi.c
@@ -83,6 +83,7 @@ static struct wmi_driver xiaomi_wmi_driver = {
.name = "xiaomi-wmi",
},
.id_table = xiaomi_wmi_id_table,
+ .min_event_size = 0,
.probe = xiaomi_wmi_probe,
.notify_new = xiaomi_wmi_notify,
.no_singleton = true,
diff --git a/include/linux/wmi.h b/include/linux/wmi.h
index da94580572a9..2d242575a8b3 100644
--- a/include/linux/wmi.h
+++ b/include/linux/wmi.h
@@ -91,7 +91,7 @@ u8 wmidev_instance_count(struct wmi_device *wdev);
* struct wmi_driver - WMI driver structure
* @driver: Driver model structure
* @id_table: List of WMI GUIDs supported by this driver
- * @no_notify_data: Driver supports WMI events which provide no event data
+ * @min_event_size: Minimum event payload size supported by this driver
* @no_singleton: Driver can be instantiated multiple times
* @probe: Callback for device binding
* @remove: Callback for device unbinding
@@ -101,11 +101,14 @@ u8 wmidev_instance_count(struct wmi_device *wdev);
*
* This represents WMI drivers which handle WMI devices. The data inside the buffer
* passed to the @notify_new callback is guaranteed to be aligned on a 8-byte boundary.
+ * The minimum supported size for said buffer can be specified using @min_event_size.
+ * WMI drivers that still use the deprecated @notify callback can still set @min_event_size
+ * to 0 in order to signal that they support WMI events which provide no event data.
*/
struct wmi_driver {
struct device_driver driver;
const struct wmi_device_id *id_table;
- bool no_notify_data;
+ size_t min_event_size;
bool no_singleton;
int (*probe)(struct wmi_device *wdev, const void *context);
--
2.39.5
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 0/6] platform/wmi: Use WMI driver core for size checks
2026-04-06 20:32 [PATCH 0/6] platform/wmi: Use WMI driver core for size checks Armin Wolf
` (5 preceding siblings ...)
2026-04-06 20:32 ` [PATCH 6/6] platform/wmi: Replace .no_notify_data with .min_event_size Armin Wolf
@ 2026-04-10 16:28 ` Ilpo Järvinen
2026-04-13 6:20 ` Armin Wolf
6 siblings, 1 reply; 9+ messages in thread
From: Ilpo Järvinen @ 2026-04-10 16:28 UTC (permalink / raw)
To: hansg, Armin Wolf; +Cc: platform-driver-x86, linux-kernel
On Mon, 06 Apr 2026 22:32:31 +0200, Armin Wolf wrote:
> As suggested by Ilpo, the buffer-based WMI API should assist drivers
> in verifying the size of buffers received from WMI methods or blocks.
> This patch series implements this suggestion by allowing drivers to
> use the WMI driver core to automatically reject undersized buffers.
>
> This avoids a memory allocation for undersized data and simplifies
> the size checking code in most WMI drivers. All patches except the
> last one have been tested using synthetic ACPI tables and appear to
> work just fine.
>
> [...]
Thank you for your contribution, it has been applied to my local
review-ilpo-next branch. Note it will show up in the public
platform-drivers-x86/review-ilpo-next branch only once I've pushed my
local branch there, which might take a while.
The list of commits applied:
[1/6] platform/wmi: Add wmidev_invoke_procedure()
commit: a6354b385fd18fcd7414052f8f6d13fd27521720
[2/6] platform/wmi: Convert drivers to use wmidev_invoke_procedure()
commit: dd2dd86f44800911e71127d3f418ee3685197c41
[3/6] platform/wmi: Prepare to reject undersized unmarshalling results
commit: 668aa6086f04d1bd63f27753d6c2d318c44ae306
[4/6] platform/wmi: Extend wmidev_invoke_method() to reject undersized data
commit: c6f69ffec0aec2ac783f900b5c17ebff13c58f82
[5/6] platform/wmi: Extend wmidev_query_block() to reject undersized data
commit: ceed8c1bcffab475facb6d4ee071464d01dd1865
[6/6] platform/wmi: Replace .no_notify_data with .min_event_size
commit: a99543f1d8f92e11ff3cb2760964490b04864e33
--
i.
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 0/6] platform/wmi: Use WMI driver core for size checks
2026-04-10 16:28 ` [PATCH 0/6] platform/wmi: Use WMI driver core for size checks Ilpo Järvinen
@ 2026-04-13 6:20 ` Armin Wolf
0 siblings, 0 replies; 9+ messages in thread
From: Armin Wolf @ 2026-04-13 6:20 UTC (permalink / raw)
To: Ilpo Järvinen, hansg; +Cc: platform-driver-x86, linux-kernel
Am 10.04.26 um 18:28 schrieb Ilpo Järvinen:
> On Mon, 06 Apr 2026 22:32:31 +0200, Armin Wolf wrote:
>
>> As suggested by Ilpo, the buffer-based WMI API should assist drivers
>> in verifying the size of buffers received from WMI methods or blocks.
>> This patch series implements this suggestion by allowing drivers to
>> use the WMI driver core to automatically reject undersized buffers.
>>
>> This avoids a memory allocation for undersized data and simplifies
>> the size checking code in most WMI drivers. All patches except the
>> last one have been tested using synthetic ACPI tables and appear to
>> work just fine.
>>
>> [...]
>
>
> Thank you for your contribution, it has been applied to my local
> review-ilpo-next branch. Note it will show up in the public
> platform-drivers-x86/review-ilpo-next branch only once I've pushed my
> local branch there, which might take a while.
Thank you :)
>
> The list of commits applied:
> [1/6] platform/wmi: Add wmidev_invoke_procedure()
> commit: a6354b385fd18fcd7414052f8f6d13fd27521720
> [2/6] platform/wmi: Convert drivers to use wmidev_invoke_procedure()
> commit: dd2dd86f44800911e71127d3f418ee3685197c41
> [3/6] platform/wmi: Prepare to reject undersized unmarshalling results
> commit: 668aa6086f04d1bd63f27753d6c2d318c44ae306
> [4/6] platform/wmi: Extend wmidev_invoke_method() to reject undersized data
> commit: c6f69ffec0aec2ac783f900b5c17ebff13c58f82
> [5/6] platform/wmi: Extend wmidev_query_block() to reject undersized data
> commit: ceed8c1bcffab475facb6d4ee071464d01dd1865
> [6/6] platform/wmi: Replace .no_notify_data with .min_event_size
> commit: a99543f1d8f92e11ff3cb2760964490b04864e33
>
> --
> i.
>
>
^ permalink raw reply [flat|nested] 9+ messages in thread