From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C71A3371CE3 for ; Mon, 27 Apr 2026 20:44:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777322670; cv=none; b=LmS0YjMMbCsTWQUX+AvB9xQdnAPdIPqeWbJZPalEFoj7FNm+wd2VophDkgvjix/YRAol7paq7wCWmukcdwwRBuEo+K2qXa+O5B+qKfJYHExo4FSecxG2qksBS817HXTy8yW0y8LUpQt1vYYNSrzZQdnS2CUSDvegWKnaYviTLY8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1777322670; c=relaxed/simple; bh=y4Qc65a6Zk5drP3D1lfp0jcqgfVIZzRU+7WknUe+epk=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type; b=FH2avyVJbqZstae/miMyKQb20TPs2yPaHS2VW8d1zdBuvxUrQd/YSoJMG5PgW1CeS68HtXV4bozvgDZwgQYL7bpPwE4xF4/7zzAOXvolZnz/tQ++VlaDSebwyP3kSqCX2tvWkiPF+di3GNwPO3SnZDoJXUurwKBxMTh9apjDqtQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=J4nIRveM; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="J4nIRveM" Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-488e1a8ac40so132252825e9.2 for ; Mon, 27 Apr 2026 13:44:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1777322667; x=1777927467; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=DlsMN5jtny510f6C8mgKbzJw/n5FmMG7ZjEs2DJD+98=; b=J4nIRveMYjp09QDdl6e++4oOn38SqkOjEbasKVfjb5oe+IyypmkkR+ALk4N5Fd2iff OA/nSdVFBAfPLJ3K+g4aecIojQFG+TS9XkBYfPc8RW9z1mhPmaatq9mMWuNICNe2gLVM /U4ue8t51qQO1ub3uJxK6m/0AzU+Zz+09znboz1/cc9zE9WeTeiXDlIB4WHJP7ZslqVc jG9vokp5fUaBau85BKPO/UhtArfquNYe8vxmqsX/WQ5ZZSbHkNXNmdh0ZPbUSkl6PZNd ChAlhkIlikMdxTcwnhFfy6vRCI/FT/87GNgQho84FnOdAyeQDZIo3ddTvKCFlM8aKFws gSyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777322667; x=1777927467; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=DlsMN5jtny510f6C8mgKbzJw/n5FmMG7ZjEs2DJD+98=; b=lVjXgIfJqX3uajbc89yfnXqdlpKOTlwspxU3E2hgNvyRJOAklRW5I/vU98ir1/EjGH Wt48pcXl4+S2VHg0haTo8aMFSvhtwB+sehbEL6YXp4zKBO0Yzeets44f0XclpDKXL5HZ uaFCJzlOgSiIUbh35zvhNDPnm4c9mdojwrtGsNttvwerbCHLaJY9EboE5gMbW35iDulX nmZ4Yuu1JURJulzFaiXLXKxWx9Hb+sZJ48/LtEqnUpFLgJkNjxB1fRuKrAVathLwVqWV tsB4mNeHPO8hcZfummFVegcWLGN3QSXZBshuVOmpbufILKUX+4yAMqVxYpmWDxRwc1II iRdw== X-Forwarded-Encrypted: i=1; AFNElJ/lC4kPByJHw2tzSRIqU0nsPKRp/HxkO8dIVeuy0vourR9S1YeQU5V89bnuRn2Hpkiokwo23e55XE8EZbI=@vger.kernel.org X-Gm-Message-State: AOJu0YzZhVOr+XfVc2FKQFh77N9ZeVh//TyVHSt219ry1btJU2qgQ+4z xAyQSHYR+eSxmvrOrhACP7jPWu9U798bkJfNfMOxZ8B21FYs79sAJnSo X-Gm-Gg: AeBDievrMSHofF6zdtHQ4v74w7TIbMTqOAwnJekGwY7m/g8v7rg9XL7r0Dz/OTDVWk1 1ZdAPlUWObnPDl8gyZ8DuKpcAaN99q6FCK7OJIkqHrajiyC0PKJFkON/zgUrLtVBnhfFyVg+V5E 7pdsbBp9bsgrxD0trcRbdJmGwAMtRf57KZd5CVdfqRnSasm+8/5vElhCO5pCua+TVZEOKrfHNNi EhzT+Z1YxOzqhRrhumxz7klLgRJ6ASEv2PWYX0Nj62NpZFlj+S6G33e9pbefdphSJQNs9VkILlg gpU6L9rWOY3IPa953te36pspJLG8SbJ++Zr0XkRuOYs3wsoUToxrXMt6oquDJE5Ey82Jaf/t9R9 7bh+Erp8EiOljsiN20YT7NTDJ93JVzz87sxj51hRMuiS5u9BY4e1f4IRlwy8PiMOn8a4916mAXp /ItiomWJZr3wz4Xf0PEA5rfgyFQM2IV1WSy1SO34/SdmPozxF+jfvW7T1th5uJjrs0FXgKm5mBN 9Youllynw3EKJKeU6pMuA== X-Received: by 2002:a05:600c:a414:b0:48a:53ea:13df with SMTP id 5b1f17b1804b1-48a77ad59bfmr3200145e9.2.1777322666885; Mon, 27 Apr 2026 13:44:26 -0700 (PDT) Received: from localhost.localdomain (200.178.141.77.rev.sfr.net. [77.141.178.200]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-4463cb59e5asm1097624f8f.5.2026.04.27.13.44.26 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Mon, 27 Apr 2026 13:44:26 -0700 (PDT) From: Hasan Basbunar To: Nathan Chancellor , Nicolas Schier Cc: Masahiro Yamada , linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org, Hasan Basbunar Subject: [PATCH] modpost: prevent stack buffer overflow in do_input_entry() Date: Mon, 27 Apr 2026 22:42:55 +0200 Message-ID: <20260427204255.22117-1-basbunarhasan@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several functions in scripts/mod/file2alias.c build the module alias string by repeatedly appending into a fixed-size on-stack buffer: char alias[256] = {}; ... sprintf(alias + strlen(alias), "%X,*", i); In do_input_entry() this pattern is unbounded across nine bitmap classes (evbit/keybit/relbit/absbit/mscbit/ledbit/sndbit/ffbit/swbit). The keybit case alone scans bits from INPUT_DEVICE_ID_KEY_MIN_INTERESTING (0x71) to INPUT_DEVICE_ID_KEY_MAX (0x2ff), which is 655 iterations; if a MODULE_DEVICE_TABLE(input, ...) populates keybit[] densely, the emission reaches ~3132 bytes — overflowing the 256-byte buffer by about 12x. include/linux/mod_devicetable.h declares storage for the full bit range ("keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1]"), so the worst case is reachable per the ABI. No driver in the current tree triggers this — every in-tree user of INPUT_DEVICE_ID_MATCH_KEYBIT populates keybit[] very sparsely (1-3 bits). The concern is defense-in-depth: the unbounded sprintf is a silent stack-corruption primitive in a host build tool, and the buffer size has not been revisited since this code was added in commit 1d8f430c15b3 ("[PATCH] Input: add modalias support", 2005). Reproduced under AddressSanitizer with a stand-alone harness mirroring the do_input loop on a fully-populated keybit: ==18319==ERROR: AddressSanitizer: stack-buffer-overflow WRITE of size 2 at offset 288 in frame [32, 288) 'alias' #6 do_input poc.c:44 Stack-canary build: Abort trap: 6 (strlen(alias)=3134, cap was 256-1) Add a small alias_append() helper around vsnprintf with cumulative bookkeeping. It calls fatal() on overflow, matching the modpost style for unrecoverable build conditions. do_input() takes the buffer size as a new parameter; do_input_entry() passes sizeof(alias) at every call site. This bounds every write into the on-stack buffer and turns the latent overflow into a clean build error if it is ever reached. Reported-by: Hasan Basbunar Signed-off-by: Hasan Basbunar --- scripts/mod/file2alias.c | 70 ++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 4e99393a35f1..c377e9a3761c 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -651,7 +651,36 @@ static void do_vio_entry(struct module *mod, void *symval) module_alias_printf(mod, true, "%s", alias); } -static void do_input(char *alias, +/* + * alias_append() — bounded printf into a fixed-size alias buffer. + * + * Replaces the historical pattern sprintf(alias + strlen(alias), ...) used + * across this file. That pattern silently corrupts the stack when the + * cumulative formatted output exceeds the destination size; with a + * maliciously-crafted MODULE_DEVICE_TABLE() input the worst-case emission in + * do_input_entry() is about 12x the on-stack 256-byte buffer. Use snprintf + * with cumulative bookkeeping and abort the build on overflow. + */ +static void __attribute__((format(printf, 3, 4))) +alias_append(char *alias, size_t size, const char *fmt, ...) +{ + size_t len = strlen(alias); + va_list args; + int n; + + if (len >= size) + fatal("alias buffer (%zu) overflow before append\n", size); + + va_start(args, fmt); + n = vsnprintf(alias + len, size - len, fmt, args); + va_end(args); + + if (n < 0 || (size_t)n >= size - len) + fatal("alias buffer (%zu) overflow on append (need %d, have %zu)\n", + size, n, size - len); +} + +static void do_input(char *alias, size_t size, kernel_ulong_t *arr, unsigned int min, unsigned int max) { unsigned int i; @@ -659,13 +688,14 @@ static void do_input(char *alias, for (i = min; i <= max; i++) if (get_unaligned_native(arr + i / BITS_PER_LONG) & (1ULL << (i % BITS_PER_LONG))) - sprintf(alias + strlen(alias), "%X,*", i); + alias_append(alias, size, "%X,*", i); } /* input:b0v0p0e0-eXkXrXaXmXlXsXfXwX where X is comma-separated %02X. */ static void do_input_entry(struct module *mod, void *symval) { char alias[256] = {}; + const size_t sizeof_alias = sizeof(alias); DEF_FIELD(symval, input_device_id, flags); DEF_FIELD(symval, input_device_id, bustype); @@ -687,35 +717,35 @@ static void do_input_entry(struct module *mod, void *symval) ADD(alias, "p", flags & INPUT_DEVICE_ID_MATCH_PRODUCT, product); ADD(alias, "e", flags & INPUT_DEVICE_ID_MATCH_VERSION, version); - sprintf(alias + strlen(alias), "-e*"); + alias_append(alias, sizeof_alias, "-e*"); if (flags & INPUT_DEVICE_ID_MATCH_EVBIT) - do_input(alias, *evbit, 0, INPUT_DEVICE_ID_EV_MAX); - sprintf(alias + strlen(alias), "k*"); + do_input(alias, sizeof_alias, *evbit, 0, INPUT_DEVICE_ID_EV_MAX); + alias_append(alias, sizeof_alias, "k*"); if (flags & INPUT_DEVICE_ID_MATCH_KEYBIT) - do_input(alias, *keybit, + do_input(alias, sizeof_alias, *keybit, INPUT_DEVICE_ID_KEY_MIN_INTERESTING, INPUT_DEVICE_ID_KEY_MAX); - sprintf(alias + strlen(alias), "r*"); + alias_append(alias, sizeof_alias, "r*"); if (flags & INPUT_DEVICE_ID_MATCH_RELBIT) - do_input(alias, *relbit, 0, INPUT_DEVICE_ID_REL_MAX); - sprintf(alias + strlen(alias), "a*"); + do_input(alias, sizeof_alias, *relbit, 0, INPUT_DEVICE_ID_REL_MAX); + alias_append(alias, sizeof_alias, "a*"); if (flags & INPUT_DEVICE_ID_MATCH_ABSBIT) - do_input(alias, *absbit, 0, INPUT_DEVICE_ID_ABS_MAX); - sprintf(alias + strlen(alias), "m*"); + do_input(alias, sizeof_alias, *absbit, 0, INPUT_DEVICE_ID_ABS_MAX); + alias_append(alias, sizeof_alias, "m*"); if (flags & INPUT_DEVICE_ID_MATCH_MSCIT) - do_input(alias, *mscbit, 0, INPUT_DEVICE_ID_MSC_MAX); - sprintf(alias + strlen(alias), "l*"); + do_input(alias, sizeof_alias, *mscbit, 0, INPUT_DEVICE_ID_MSC_MAX); + alias_append(alias, sizeof_alias, "l*"); if (flags & INPUT_DEVICE_ID_MATCH_LEDBIT) - do_input(alias, *ledbit, 0, INPUT_DEVICE_ID_LED_MAX); - sprintf(alias + strlen(alias), "s*"); + do_input(alias, sizeof_alias, *ledbit, 0, INPUT_DEVICE_ID_LED_MAX); + alias_append(alias, sizeof_alias, "s*"); if (flags & INPUT_DEVICE_ID_MATCH_SNDBIT) - do_input(alias, *sndbit, 0, INPUT_DEVICE_ID_SND_MAX); - sprintf(alias + strlen(alias), "f*"); + do_input(alias, sizeof_alias, *sndbit, 0, INPUT_DEVICE_ID_SND_MAX); + alias_append(alias, sizeof_alias, "f*"); if (flags & INPUT_DEVICE_ID_MATCH_FFBIT) - do_input(alias, *ffbit, 0, INPUT_DEVICE_ID_FF_MAX); - sprintf(alias + strlen(alias), "w*"); + do_input(alias, sizeof_alias, *ffbit, 0, INPUT_DEVICE_ID_FF_MAX); + alias_append(alias, sizeof_alias, "w*"); if (flags & INPUT_DEVICE_ID_MATCH_SWBIT) - do_input(alias, *swbit, 0, INPUT_DEVICE_ID_SW_MAX); + do_input(alias, sizeof_alias, *swbit, 0, INPUT_DEVICE_ID_SW_MAX); module_alias_printf(mod, false, "input:%s", alias); } -- 2.53.0