From nobody Mon Sep 28 21:52:49 2026 Received: from mail-lf1-f53.google.com (mail-lf1-f53.google.com [209.85.167.53]) (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 F030E3BBFCC for ; Mon, 17 Aug 2026 08:18:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786954683; cv=none; b=CpWyann/yuJu8k0DcPCEFpodawA7hAAViHpefs+APDVrhpqCbArEWseTuFlsyJDNiC+d8h6WsOOHv+Pzsjaw8b5SpKiN7GiDQoY/MxSUpcpnIBNupXmGtcm/CCZgFbIcy8Zo7xSjEkNInaCu5z3vttbNiefQ2xzqDslZhD+BRgM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786954683; c=relaxed/simple; bh=lwXAQeijo31yQP352IYmI6t5ooOevLP8dvey8N0IrDo=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=dxXAnA9CDCSwIYCAOGybuv8wE9k1rttenosPH85eGieew0WvlZbQ6cekfthck2XPWJN/XsTtBSDjdHlUiwcvc0G+MGNMVOYoCudMqn3oldQ3HE6eXwEQufism92jRtDp+gT/kiopQHhy5OyoFbYyaP11gm0iccebwNYGObZJXyQ= 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=n+gfHkEF; arc=none smtp.client-ip=209.85.167.53 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="n+gfHkEF" Received: by mail-lf1-f53.google.com with SMTP id 2adb3069b0e04-5b28c91fba5so3703800e87.1 for ; Mon, 17 Aug 2026 01:18:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786954679; x=1787559479; 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:content-type; bh=Kr4krNOjyqQplmnPBqykD0MinGwk5XrmWVr9/hkIl1o=; b=n+gfHkEFfQR9c8YibKPLJZrUqey2eELGWD+3yx27x1GAIaz3j68H+XCEnyDaj5np2d WN0RZwlXYi2/zFoogSffHNNgbXqnGVOn5h/JrO1Ze9a/3LmTxDhrJ/Ysw5ZBYeIKztjO 14YSCAwdk+CzJZ1M6NS40cRCdc8vonTmHh33P2X872FjdbBrfC55knvT3z10XIByjAMX jDlOWTVxTL4B9gsfTyLmsy4omFsdTgBEpmDgtVZyFToXpfH8QXVnpOQc5bUdkmdhEL07 uALfGqEcxS/4tHYWA+EzAnU1YypM3K5bysQLwckm/j5i2y8Q/rMBrCq4Q59Fu+usHP6j 1yBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786954679; x=1787559479; 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:content-type; bh=Kr4krNOjyqQplmnPBqykD0MinGwk5XrmWVr9/hkIl1o=; b=Ml2QZl43S9kKV2TKkBB8iHRFTx3phHNXkGMcYWIlUwXN2Sdm/v20oXl7npdZS7WJ/x GqlP9ZE9uzaSqhkdTbPUYeNYQoJRVXHcti4YwxzHr8SUyB1OdJCHZV/Vwz22HNedA79+ f5vQRVJjEuEor7XJ4+xb2Qwcmlaa9+6wWkcbxyWSwx+5VOwUm2h+cLEfLGkyw5yCwx1O qo69ifcKg0l1W+apRt+uI1R5YvhqI76UfGHi7qb71LSTPa7tThwe7aY03leY4ORqvgVR /ui8amVa8tZBh10m1pCGrLub9m5tZq6b5eqiSV9KZESVdBkUf6Cc0Fu6u96OnXked1RC m1Bg== X-Forwarded-Encrypted: i=1; AHgh+RpV5GJ2agrOgs5yQHQnRo/Jwy1bws/y+p14E/MJtkK/d3yw5FdqjWvxrSmyhisldQoD67N+5msct4VX/eg=@vger.kernel.org X-Gm-Message-State: AOJu0YwvPoB+Hx5bxXJg45gugILJS9xEz/p7XrmZfQDqhg0AwIr8VBcF zlBUCpQ2gL70sDtxuZ2pr1nLMH4llFHMFe3TE8aWW/LA4WVbwRPk3Fdc X-Gm-Gg: AR+sD10dPWuq4o6J2Vq913JezbUDMW8yF9s6c66XeAHhu7Q2yJvOJmpH3xUm9qlKTQ5 9OXWgZa/iwRZq1nL5BiwkcJzWPc3UvWFqiavj1QVYwOGfRibVZRhaG222PqONpon6efAHP9P+yT HvUAku4M4EdGJuXyTKJAZPu9kHWc1+Yc+p5FnKuumYmweO5U5ZuP3sCUTCBSBEL/BfLtQV4bdqS /hOZNsX6pJ16LcsgnFSsljvhbAgUldpIe4b6XegM5vjVoaCIsJB3qJaGWOIEN/FwCjQd8Jvznzp 9h+aRDbvr/k4e80P2i/BUBG0xfVk1MjAnog8+EkmwzizEQrO/1AXPWIv5oG+xMGorm+QNIL/uLt LF/YnVQz3utcqih6nOgWj9BycXC4pGYO62TRE8HelxMEkK2wY29X51vs6lIZFEvzhkRBG1fqkCq AaSazGGeIOizfTh4UcfM/noYDxodjhuljaZ5i8HCFn5zn6 X-Received: by 2002:a05:6512:8384:b0:5ae:b7c7:5334 with SMTP id 2adb3069b0e04-5b453cd9ca7mr4540296e87.17.1786954678709; Mon, 17 Aug 2026 01:17:58 -0700 (PDT) Received: from c.. ([213.165.253.80]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-5b46cfa12c1sm199089e87.24.2026.08.17.01.17.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 Aug 2026 01:17:58 -0700 (PDT) From: Narek Jilavyan To: Alexander Viro , Christian Brauner Cc: Jan Kara , Mateusz Guzik , linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Narek Jilavyan Subject: [PATCH] fs: do not cache a symlink length that disagrees with the string Date: Mon, 17 Aug 2026 08:17:56 +0000 Message-ID: <20260817081756.4176757-1-njilav@gmail.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" inode_set_cached_link() stores a caller-supplied length in i_linklen and sets IOP_CACHED_LINK. vfs_readlink() then uses that length directly: if (inode->i_opflags & IOP_CACHED_LINK) return readlink_copy(buffer, buflen, inode->i_link, inode->i_linklen); and readlink_copy() clamps only against the user buffer, not against the string, so a length larger than the symlink body becomes a copy_to_user() of adjacent kernel memory - reachable by any process calling readlink() on such a symlink. The only thing standing behind the invariant is VFS_WARN_ON_INODE(strlen(link) !=3D linklen, inode); which expands to BUILD_BUG_ON_INVALID() unless CONFIG_DEBUG_VFS is set. On a production kernel it type-checks the expression and evaluates nothing, so the value is stored unvalidated. All four in-tree callers are correct today, and notably the two whose length comes from on-disk metadata (fs/ext4/inode.c, fs/erofs/inode.c) both re-derive it and reject the inode rather than relying on this helper. The API should not require that of the next caller. Validate unconditionally and fail safe: if the length disagrees, warn and leave IOP_CACHED_LINK clear. i_linklen has exactly one reader in the tree and it is gated on that flag, and vfs_readlink() falls back to i_link with a strlen() of its own, so the inode degrades to the behaviour that predates the cached length instead of disclosing memory. The check runs once per symlink inode setup, not once per readlink(), which is what the cache was for. Fixes: ea3821990719 ("vfs: support caching symlink lengths in inodes") Signed-off-by: Narek Jilavyan --- include/linux/fs.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/linux/fs.h b/include/linux/fs.h index 50ce731a2..e1d8f2614 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -946,9 +946,19 @@ static inline void inode_state_replace(struct inode *i= node, =20 static inline void inode_set_cached_link(struct inode *inode, char *link, = int linklen) { - VFS_WARN_ON_INODE(strlen(link) !=3D linklen, inode); VFS_WARN_ON_INODE(inode->i_opflags & IOP_CACHED_LINK, inode); inode->i_link =3D link; + + /* + * i_linklen is used as a copy_to_user() length by vfs_readlink(), so it + * must not be taken on trust. If it disagrees with the string, leave + * IOP_CACHED_LINK clear: vfs_readlink() then falls back to i_link and + * recomputes the length with strlen(), which is what it did before the + * cached length was introduced. + */ + if (WARN_ON_ONCE(strlen(link) !=3D linklen)) + return; + inode->i_linklen =3D linklen; inode->i_opflags |=3D IOP_CACHED_LINK; } --=20 2.43.0