From nobody Fri Jul 24 23:30:20 2026 Received: from mail-pj1-f51.google.com (mail-pj1-f51.google.com [209.85.216.51]) (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 C60A94A2E21 for ; Wed, 22 Jul 2026 09:57:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784714230; cv=none; b=IWs7yuPO56sYCKaP8iqg9G9Ljvt20VlrdYySsR9kM236kMtlyy/iKN54fz/Pr9O/voaW5ykR8fCXyMwvzgyDq0BNppXC+nb0u9Lq8/ja4HaUsVw8F4L8xHqIZG4i+V4+fzfTvvnok/OEN5JnB4b/GJBpjYwVEZcK/o+Z+CM6EjM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784714230; c=relaxed/simple; bh=3pH63PZrNIrrK9dHFELvurvMG28vzwC0HG3QJj/W1Eg=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=Xz35XZaMgOWM1r8HXPgr8ePbE8BT/PMJLQmvN+xveg8R0R4q3mqKJ8PiLMTq4a94fWGWm2p6qxXkBB6IteWQRquzQMB62/KQHb9FGSHtYcnewK/ZskyW2tXROV6pKpi4lY4uBtiQyg4ZBOwEckg265aLXAI4G5PNUs05zRE9ZZU= 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=lN0Xl8JT; arc=none smtp.client-ip=209.85.216.51 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="lN0Xl8JT" Received: by mail-pj1-f51.google.com with SMTP id 98e67ed59e1d1-38e69bdb0fcso2889006a91.1 for ; Wed, 22 Jul 2026 02:57:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784714226; x=1785319026; 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=qlo6v/j1mktOqnKmy44ulbYNPMM7z5pnpSzY1f8OpC4=; b=lN0Xl8JTQnIZs26ANuUHuQR2zaqnqrM3lq6CItENpC9dBlq8Lx/ZEx6HWGC3EWh2je KZ9skkRtFPt9FxRQ9Uj85cB1VvLkFUnsDyqz8bCCOwrjJk6+3M3Tr2iUrxKxZd85kRtV uZW4GwhGRYRBsYckndlj1i/oIckJ1Bc7P4x+6TLSf1BDKafDMPMXeQBtpu0v4H4+0OxL Amlkvb+ZOfif6c5VUZnKnRpXnfmqhDUp5aQm9vpo/ruAeKJUpnosHWrCSQ1yjxZcMR7N 61mr2AB+ehChT1I4zlM7QA3s4p1GPtslR0a+sktqQSitNbq567HnuD0Sy9SrzaG8Fvll cOoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784714226; x=1785319026; 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=qlo6v/j1mktOqnKmy44ulbYNPMM7z5pnpSzY1f8OpC4=; b=UFDkCqCuPAP7xljE4B0cJaCSUJilqOS7A9sdI06HJZHHXwKTEvYM6IQ74MPeNNRSV+ ZHKVO9LRM7V3eTnnpJP2e3k88if3E0Z+spHnXXUMhOnXT3KHsM7xpz0yn4x2tlgtwOEl KQ/bR1IpbtOshxYNC3D00uo82AKvJ93PHTEmDSddDvonKLVegAkWzqWIdne7C/b6Id1U 6K1+BES5Mt4XZwt2jGQFKjENJzkNEOv6FhW/HBDhMWlXNcso/zk4LdXd2BuQbhFXFq9s 8/FxdgSkGyK8AOooot7xMyun4aJVerR8C6LHIU5kxbp34wGxHFTfG5uKY/ULEwSumQv7 g8ZA== X-Gm-Message-State: AOJu0YwQJ4+xmAXslyGBU7HJzcrvN4Bsz2E3NhhrYWuP4LPawq2zU1+s GI0vx+LlXpdlzJeB9cIZaGUf+dSmVKLUE7pXlyDZZLLl2GKD2+P2EmIJjnQK3Q== X-Gm-Gg: AR+sD11S8+BmZerc2OAV6MsPreZ1HuTdVAQwsmEsZyV6HaTnwmwgehE8lsr0f9J2cvX wGb79ba0IEVvQdQD0N1qgAWzdRmr58OB4xC7Xu1LhXuKD9A7xLdMkirMLp/4GifFYG8ezRpHo8P uZWoPRAfK5oPL4JqazK0vTXRsMfz7LBX92FtJeye3LX1RIQR83xaHpl4Ek8zGK9GGhs/XqsZxDI Ut15fouVchYusaBhNSDocq2KXixX7UY0zfB4LZ19kXhn1ldW3ho6DpObHtZ/DWQLEOqGrPmOp8O YYxuuUz4aPOZG9dNJX2qcSnhYjf1cCokcAYsKb+WEnV2ynka3kOVkI9BfgSTi9ckbl+McujO5JK bnPmFxl65fFCffbabUnMy7xDEww/e2tZOIOPoey964BUHulKT/Cvo9s7ywzcBoqOdfYEA+Lz8fg YFkyE+rjIP2r8m/jUhx0/nvCtznSfHVQvBqExHW7mItpvlapxZJ4ec X-Received: by 2002:a17:90b:2890:b0:36d:7b62:4767 with SMTP id 98e67ed59e1d1-38e4b434f04mr22899707a91.5.1784714226352; Wed, 22 Jul 2026 02:57:06 -0700 (PDT) Received: from DESKTOP-L3Q0GIV.localdomain ([203.230.195.19]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-38eab3dbcacsm572378a91.3.2026.07.22.02.57.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 22 Jul 2026 02:57:05 -0700 (PDT) From: Sangho Lee To: linux-kernel@vger.kernel.org Cc: Sangho Lee , stable@vger.kernel.org Subject: [PATCH] romfs: reject directory entry cycles Date: Wed, 22 Jul 2026 18:57:02 +0900 Message-ID: <20260722095702.1382671-1-kudo3228@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" A ROMFS directory stores the first entry offset in ri.spec and links its entries through the next field in each on-disk file header. Both romfs_lookup() and romfs_readdir() follow this image-controlled chain without checking for cycles. A crafted image can point an entry back to itself or to an earlier entry. Looking up a missing name then loops forever in romfs_lookup(), consuming a CPU in kernel context. This is independent of hard-link cycle handling: directory next pointers and hard-link spec pointers are separate chains. Validate each directory entry chain when the directory inode is instantiated. Use Floyd cycle detection so validation needs constant memory and does not impose an arbitrary directory size limit. Reject a cycle with -ELOOP before lookup or readdir can traverse it. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Sangho Lee --- fs/romfs/super.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/fs/romfs/super.c b/fs/romfs/super.c index ac55193bf..061275761 100644 --- a/fs/romfs/super.c +++ b/fs/romfs/super.c @@ -96,6 +96,51 @@ static const unsigned char romfs_dtype_table[] =3D { =20 static struct inode *romfs_iget(struct super_block *sb, unsigned long pos); =20 +static int romfs_next_dirent(struct super_block *sb, unsigned long pos, + unsigned long maxoff, unsigned long *next) +{ + struct romfs_inode ri; + int ret; + + if (!pos || pos >=3D maxoff) { + *next =3D 0; + return 0; + } + + ret =3D romfs_dev_read(sb, pos, &ri, ROMFH_SIZE); + if (ret < 0) + return ret; + + *next =3D be32_to_cpu(ri.next) & ROMFH_MASK; + return 0; +} + +static int romfs_validate_dir_chain(struct super_block *sb, unsigned long = pos) +{ + unsigned long maxoff =3D romfs_maxsize(sb); + unsigned long slow =3D pos; + unsigned long fast =3D pos; + int ret; + + for (;;) { + ret =3D romfs_next_dirent(sb, slow, maxoff, &slow); + if (ret < 0) + return ret; + + ret =3D romfs_next_dirent(sb, fast, maxoff, &fast); + if (ret < 0) + return ret; + ret =3D romfs_next_dirent(sb, fast, maxoff, &fast); + if (ret < 0) + return ret; + + if (!slow || !fast) + return 0; + if (slow =3D=3D fast) + return -ELOOP; + } +} + /* * read a page worth of data from the image */ @@ -292,6 +337,13 @@ static struct inode *romfs_iget(struct super_block *sb= , unsigned long pos) pos =3D be32_to_cpu(ri.spec) & ROMFH_MASK; } =20 + if ((nextfh & ROMFH_TYPE) =3D=3D ROMFH_DIR) { + ret =3D romfs_validate_dir_chain(sb, + be32_to_cpu(ri.spec) & ROMFH_MASK); + if (ret < 0) + goto error; + } + /* determine the length of the filename */ nlen =3D romfs_dev_strnlen(sb, pos + ROMFH_SIZE, ROMFS_MAXFN); if (IS_ERR_VALUE(nlen)) --=20 2.43.0