From 51b3d8d53a4a6e561a7bb071ee2b5ed84688994a Mon Sep 17 00:00:00 2001 From: Jan Baisch Date: Wed, 12 Aug 2026 17:38:42 +0200 Subject: [PATCH] platform/surface: aggregator_tabletsw: accept zero-padded source-list responses Some Surface firmware returns a fixed-size POS source-list response with unused bytes zero-filled instead of returning only the bytes described by the source count. On the Surface Pro 12 Intel, the POS source-list command returns 24 bytes with count 1, source ID 0 for the Type Cover, and all remaining 16 bytes set to zero. The current parser only provides 20 bytes of response capacity and requires the response length to exactly match 4 + count * 4, so the POS tablet-mode client fails to probe. Provide space for the observed additional trailing word and allow trailing response data only when it is entirely zero. Also validate the source count against the fixed source array before calculating the expected payload size. Exact-length responses remain accepted unchanged. Short responses, source counts larger than the available array, and non-zero trailing data continue to fail with -EPROTO. This allows the Surface Pro 12 POS tablet-mode client to bind while keeping the parser bounded and rejecting unexpected response data. Link: https://github.com/linux-surface/linux-surface/issues/2144#issuecomment-5264372177 Signed-off-by: Jan Baisch Link: https://github.com/linux-surface/kernel/pull/173 Patchset: surface-sam --- .../surface/surface_aggregator_tabletsw.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/platform/surface/surface_aggregator_tabletsw.c b/drivers/platform/surface/surface_aggregator_tabletsw.c index ffa36ed928970..25b798fcb1001 100644 --- a/drivers/platform/surface/surface_aggregator_tabletsw.c +++ b/drivers/platform/surface/surface_aggregator_tabletsw.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -328,6 +329,7 @@ MODULE_PARM_DESC(tablet_mode_in_slate_state, "Enable tablet mode in slate device #define SSAM_EVENT_POS_CID_POSTURE_CHANGED 0x03 #define SSAM_POS_MAX_SOURCES 4 +#define SSAM_POS_SOURCE_LIST_PADDING_SIZE sizeof(__le32) enum ssam_pos_source_id { SSAM_POS_SOURCE_COVER = 0x00, @@ -353,6 +355,7 @@ enum ssam_pos_state_sls { struct ssam_sources_list { __le32 count; __le32 id[SSAM_POS_MAX_SOURCES]; + u8 padding[SSAM_POS_SOURCE_LIST_PADDING_SIZE]; } __packed; static const char *ssam_pos_state_name_cover(struct ssam_tablet_sw *sw, u32 state) @@ -477,6 +480,8 @@ static int ssam_pos_get_sources_list(struct ssam_tablet_sw *sw, struct ssam_sour { struct ssam_request rqst; struct ssam_response rsp; + u32 count; + size_t expected; int status; rqst.target_category = SSAM_SSH_TC_POS; @@ -501,8 +506,15 @@ static int ssam_pos_get_sources_list(struct ssam_tablet_sw *sw, struct ssam_sour return -EPROTO; } - /* Make sure 'sources->count' matches with the response length. */ - if (get_unaligned_le32(&sources->count) * sizeof(__le32) + sizeof(__le32) != rsp.length) { + count = get_unaligned_le32(&sources->count); + if (count > ARRAY_SIZE(sources->id)) { + dev_err(&sw->sdev->dev, "too many posture sources: %u\n", count); + return -EPROTO; + } + + expected = sizeof(sources->count) + count * sizeof(sources->id[0]); + if (rsp.length < expected || + memchr_inv((u8 *)sources + expected, 0, rsp.length - expected)) { dev_err(&sw->sdev->dev, "mismatch between number of sources and response size\n"); return -EPROTO; }