From nobody Mon May 25 06:41:33 2026 Received: from mail-qt1-f176.google.com (mail-qt1-f176.google.com [209.85.160.176]) (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 C9247351C22 for ; Sun, 17 May 2026 11:10:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779016236; cv=none; b=LJXttUtv8d9aXlZzrudccY63KIpPQk4w213iodC31YNW8xaBZwCGGrVmBDsKC7CfNet8dB0GrsA5+gF8nWuWls1VgGGDG/g9BLe7VhdxSUk6Zf+8fMiAspGGVhuiwA5nxZo+kyrJ5lRvsaQ5TJMBZ4mriR5lOQe1UjoI7hWttXg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779016236; c=relaxed/simple; bh=KBKpFoApqVOYrZgt8hDeAf2QScaQBhG6btxplHe/0IM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jcaK+yT8In1LoFqJgy/BM2N9Aa4+qW+wizeVUXox/n+JBsBGgBmBJlQlt5CpkI6a3n9/vYXlWB6a3YaoI6TKmlg1aSKYLrQZ1tA+5kpDFcseainOcZLw8Vo4EEbTjaZYZQsQt+WE1x2PlSBFsg+5Z4jxtqQRlxBHUX5zjRP1sJ0= 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=DkqqmM8n; arc=none smtp.client-ip=209.85.160.176 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="DkqqmM8n" Received: by mail-qt1-f176.google.com with SMTP id d75a77b69052e-51306c36c3eso18856751cf.0 for ; Sun, 17 May 2026 04:10:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779016234; x=1779621034; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=lYwK9wCTOrjldULnc69mQhALgu/e64LYTBbU5tr14QM=; b=DkqqmM8nvzdU/R9mGP2zN7qEv0bMNd3p7DigMG30L24VS7g5WAYDIAO1Mr90Q8H0Tl Ei+gUFbtm/RjDN61ho5qTvkcRPGlVXAMsxigt/W3aUzI9UhI23Sk8F4z6RQoo3MPqD/Y Tv3b1mSbhpJZmgWn0gorpHXLitEFmMma4J2tt4PoJl9lyDiIhQpjvJ+EHw7aEnKO2eFt b0CPPVcPkRXViJAZmb236BMO+LYn1OwKcl9AU72ysmHqpgbOKmMGzr3Z7/qSxdBJrk1Z 58ygmvl7WPFOty8ZG4exJbM2UogSgLpoVsln7Gxo/nA6mW3cKpQFen8hkoj+xo7cSt8M kLUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779016234; x=1779621034; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=lYwK9wCTOrjldULnc69mQhALgu/e64LYTBbU5tr14QM=; b=MBdoB3CL+ziHBxjklnVaUpWYmNoQ4m67CTHDvkEXvo85nyk+Hzmd0oIRafw6zimTQh RcwC7y7Ea6I+bYB2w67FK6DVAEdaAvrctl5wgCYtUQUPPM2y3kQVqwq9+OKq8GNZMQIu vhfComjsxCn/JZmiUgDKkS21cSMjMVYlWBCFHtp0ftklhXporH94xFVSfTHWXL8oMKmH jIQb9tshRP0oUbByioQgWmuJh/dh2CuAEg/3CpPtuG85nnj65MRsgQR24jitZqGODZsl 79ElC25dY8A8y8hX2IIFsqGbCDkK1Q8N9cRgNnlp/rbQ+1UHGBQbdxtcGiv8VE4wJ+Qj YArA== X-Forwarded-Encrypted: i=1; AFNElJ8bCXeDX0SHdO2zmWakBBxoO6Oi0wUOG2TouRfC7tBjuESk4z+iQOl8Pss7gJd0kKvxMfCLMi64jHRymjc=@vger.kernel.org X-Gm-Message-State: AOJu0Yw/J4s5+g9mOzlSOQ/+qviHQuVC5Z8MD2aIrivpqllzeHbswINa 0hXk9pugbtB5IvQ3qCBu/AQhHFVueadLYneDjuoRELC23GgFs33SZ260 X-Gm-Gg: Acq92OEtNkIpeAIR35C+XnzEZkb43qKU8pwtA931C55c2upvCVeAzOZ6LmuDVrLL6/m C/WwEROHVofVkLVKnvHv4dpHPF61zAlFRTFtgiAbBnFddfHuFa2FbvKt0WfnsALDfUz5TPbb5cm DgQS1tXjlnM/BGrjtON7I09vemAuFsxbiy63l+b9SeNdoCxbVOPZl3b15FFi15vz1y1p8Ce/hus QmRyh0zIjt5WolMiUknIrq3WSkH1g01qHzQHKMVBhBYBySJqxwQ3hk/4Bejl2WMC9yQvK2fDKE3 RwcGyNvBm2rs5bwhPBMlFxHcIJ5mTUbhE3gwem3dLHQJCpWT6iYazPRRqBGSV2XdVfhSc1eFVE6 ddicD+FIDHXkT3aKVY8ReoQI4A0BDCpJ2qwYD2XEPjsEr5ZXYNVc/IXp5JZt/WFqpRoSzemZBfI JjA+FIda+/z0YQs96KDEvgc1gFEFiJF63GszvCkgIXV2BpqdLYlqmavkZ5P/8IoMo/vSuy5EuQG Zf4710HPTk8zdZzMbWVj5jjWb30SVukzVI1OQaT9KU= X-Received: by 2002:ac8:57ce:0:b0:50f:340f:ff37 with SMTP id d75a77b69052e-5165a0df14dmr151793751cf.22.1779016233726; Sun, 17 May 2026 04:10:33 -0700 (PDT) Received: from server0.tail6e7dd.ts.net (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-516456df09csm100306651cf.13.2026.05.17.04.10.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 17 May 2026 04:10:33 -0700 (PDT) From: Michael Bommarito To: Joseph Qi , Mark Fasheh , Joel Becker Cc: ZhengYuan Huang , ocfs2-devel@lists.linux.dev, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/3] ocfs2: reject dinodes with non-canonical i_mode type or stray bits Date: Sun, 17 May 2026 07:10:12 -0400 Message-ID: <20260517111015.3187935-2-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260517111015.3187935-1-michael.bommarito@gmail.com> References: <20260517111015.3187935-1-michael.bommarito@gmail.com> 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" ocfs2_validate_inode_block() currently accepts any 16-bit i_mode value as long as i_mode is non-zero. ocfs2_populate_inode() then copies that mode verbatim into inode->i_mode and dispatches on i_mode & S_IFMT to the file/dir/symlink/special_file iops; any unrecognised type falls through to ocfs2_special_file_iops and init_special_inode(), which interprets id1.dev1.i_rdev as a device number. The result is that anything able to forge or corrupt an inode block (a hostile cluster peer with raw write access to the shared LUN, a privileged user mounting an attacker-supplied image, on-disk corruption) can publish an in-core inode whose type bits do not name a POSIX file type, or whose permission bits carry bytes outside S_IFMT|07777. Both shapes propagate into VFS-visible state that downstream code paths assume is well-formed. Reject early in the validator: - mode bits outside S_IFMT|07777 - S_IFMT values that are not one of S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFIFO, S_IFSOCK mkfs.ocfs2 and the kernel only ever produce these seven types plus the standard permission, setuid/setgid/sticky bits; an on-disk i_mode outside this envelope is structurally malformed regardless of how it got there. Validated against the existing inline_data, refcount, and chain-list checks: this hardening fires before any of them and does not perturb their behaviour for well-formed inodes. Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function= .") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi --- fs/ocfs2/inode.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index a510a0eb1adcc..fb592bf3e5f31 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1494,6 +1494,45 @@ int ocfs2_validate_inode_block(struct super_block *s= b, goto bail; } =20 + /* + * Reject dinodes whose i_mode does not name one of the seven + * canonical POSIX file types, or whose mode carries bits outside + * S_IFMT | 07777. ocfs2_populate_inode() copies i_mode verbatim + * into inode->i_mode and then dispatches via switch (mode & S_IFMT) + * to file/dir/symlink/special_file iops; an unrecognised type + * falls into ocfs2_special_file_iops with init_special_inode(), + * which interprets i_rdev. Constrain the type byte here so the + * dispatch only ever sees a value mkfs.ocfs2 / VFS can produce. + */ + { + u16 mode =3D le16_to_cpu(di->i_mode); + + if (mode & ~(S_IFMT | 07777)) { + rc =3D ocfs2_error(sb, + "Invalid dinode #%llu: mode 0%o has bits outside S_IFMT|07777\n", + (unsigned long long)bh->b_blocknr, + mode); + goto bail; + } + + switch (mode & S_IFMT) { + case S_IFREG: + case S_IFDIR: + case S_IFLNK: + case S_IFCHR: + case S_IFBLK: + case S_IFIFO: + case S_IFSOCK: + break; + default: + rc =3D ocfs2_error(sb, + "Invalid dinode #%llu: mode 0%o has unknown file type\n", + (unsigned long long)bh->b_blocknr, + mode); + goto bail; + } + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data =3D &di->id2.i_data; =20 --=20 2.53.0 From nobody Mon May 25 06:41:33 2026 Received: from mail-qt1-f179.google.com (mail-qt1-f179.google.com [209.85.160.179]) (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 F1B6135F607 for ; Sun, 17 May 2026 11:10:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779016237; cv=none; b=ETpmYo4ac5TsZgHPawyQcJLw4CUG0Q8ibFnlbzSoEsFVMCimv7mVaLu8+bdHE21YFKNlCcHoYKB+nQ+Kd9a4/yZmV0De5HaLtUnC7PRPfpfKu7jkCUojoP+VTb/jRuNppfvu5mK0Cy1QKG4CkvwT+ijBvcH5UL/FN/TR54ygOyQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779016237; c=relaxed/simple; bh=n+C1rVyGdlKurjuMZfviPdnXsf7j/DTvcv2LKJqNXf4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XDnQvZAJ00CX/bxgJLM77oz4V+6UpnhjkhxVRJ80z2qu4BuNfl5xny/tnH9LXltnaLrwnhfYku57iWEcMXWayLwpBD9a7kSJxWeNOVv7agww/qVrAmfjlttAYzOJoqzGheBaFuVWwL2qJ960Szm8fEkm0a5tjWzJUl55DhM67YU= 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=OnWj+u7w; arc=none smtp.client-ip=209.85.160.179 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="OnWj+u7w" Received: by mail-qt1-f179.google.com with SMTP id d75a77b69052e-50e63771eb0so17984301cf.3 for ; Sun, 17 May 2026 04:10:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779016235; x=1779621035; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=N7Mwcd+TqqhYeRK2C3/0bLC/BLmg36DmKBF7VQkSkpQ=; b=OnWj+u7wfnaYSZ6yp4g4M3kIcj4bk1nw8Am5rvgeKf8WGnwwkXgrYwBoOI5vGouJJ1 yP4GL3LA3V5kVcuNeSMQEXWNxjiktuLSBTC6mqwZZBGGOv7jl39qZLWGi3M/nqMtHL3Q bAUBn3WU86MwUOzDsK7ICV6pid1RA+nTffLmLZu44SK5o+LuhkfSTWADezD2MJ99bTpn A6l2fMygsHg6Ntu+L7usfXkJ+oD3e/ORooW569eVid+yz0mq/gqwk+Qvkn0Y1+o75HSW 3L0pUB7geVR/36woy6pVOcB9w9qzjslexM8/naCIfQuS0zpFwf5x06SH9aUZY/vmDRXi ijaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779016235; x=1779621035; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=N7Mwcd+TqqhYeRK2C3/0bLC/BLmg36DmKBF7VQkSkpQ=; b=maPnqBSWu6VNHwgrG66SijR7LnU2AtXFTIJantIq/1291L8glrv59n6akqMxUcZ0PS tcHVKHb46vbpB0s4CsP80TfLZVWh/iqpRt8jI/IQaqpudgc5MQE20n474nFQy7HOaJDA 00saVGloiEyIUvdFZD6Gckr6xp9FAudBdlDucASWqoxPRtF4i2ZmiFKEb8wrp/TmhMCt lDPGl28wHzJUvfkrQcVMZQgMGG/1lA9I8DebF05nQqJ0sPNKHHvQSq+nOFTjCpmp4a2B Bc0E083oD9XZGxcRObwW/4CuecSXhQpSoBIw0k+91YdmKmf8p1DgoWrvvVw43jh7TK4M XfgA== X-Forwarded-Encrypted: i=1; AFNElJ/mKwN0TbIUYSgtUvDU55ZUQhZgqK0LGlxyc+osCdBYw/n/2cDB+gK2v4XTYQoGJoDCr+AzY38c4sqDbFg=@vger.kernel.org X-Gm-Message-State: AOJu0Yz0CEyjnjmkCNgcSXN3jort1AtpVha2bd0p19aAVFmwjyDWC4Xi gmLiM0pQ+rw6dKQTKK3Bge+EaESc7duCZ/GYrwfEAVPA1pLpgg7VNOPc X-Gm-Gg: Acq92OFiyeGd9kEYe/DhkpUNQ+6FsOQM+lOhfjO4CYBlgmPCbhN9oI8WaDbEeEP6zVV QQ8R5sMiBbwTQbkrsP6GD6lXYiTeo/wf3YdP4Iqk79fdqcN5bAafVBmUPdWhbLv2viirWT74oZl CQMFwo/gHRy4Qhh63okTLPLo/ZnP8iyzVesg0YAthzZOt1mA1m+IafHfD/a/e0MtwzMihsDAOSk 36YILz44Gl6IYqk55fEZHTCWCCvjRS0SXkzMeE4Uvym5CcpwDTrfLSrGauztQIATwV3yJqyyCMo NwFek+Vu/Fmkk8Orl+pShPZNioqsPdOuBddsZ51Y/AybwRfGZWn7CqpRbYjof7MNcMyOzbN5nV4 6kg8dennowxpcPVi8qBL+ddx1NKQyFUN82IgDLxGR0/XPTXtIZruBPyRMRXhojyvOrAEz6rzTQA qrE52PyRJfgsgwIgLCxr2BVs4Fjt8s8hFSMPJ9/UdQeUQJhd4l53FSgYb9g/PqjmnlpIMEK2KMC ri5l8kfvCzk7eLvV8vDjhE5f5Xn14NprZSu7m6Ud+E= X-Received: by 2002:a05:622a:6201:b0:50f:ade2:2d3 with SMTP id d75a77b69052e-5165a1ec441mr152988931cf.42.1779016234888; Sun, 17 May 2026 04:10:34 -0700 (PDT) Received: from server0.tail6e7dd.ts.net (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-516456df09csm100306651cf.13.2026.05.17.04.10.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 17 May 2026 04:10:34 -0700 (PDT) From: Michael Bommarito To: Joseph Qi , Mark Fasheh , Joel Becker Cc: ZhengYuan Huang , ocfs2-devel@lists.linux.dev, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/3] ocfs2: reject dinodes whose i_rdev disagrees with the file type Date: Sun, 17 May 2026 07:10:13 -0400 Message-ID: <20260517111015.3187935-3-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260517111015.3187935-1-michael.bommarito@gmail.com> References: <20260517111015.3187935-1-michael.bommarito@gmail.com> 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" id1.dev1.i_rdev is the device-number arm of the ocfs2_dinode id1 union and is only meaningful for character and block device inodes. For any other user-visible file type the on-disk value must be zero. ocfs2_populate_inode() currently runs inode->i_rdev =3D huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev)); unconditionally, before the S_IFMT switch decides whether the inode is a special file. As a result, an i_rdev value present on a non-device inode is silently published into the in-core inode. A subsequent forced re-read or in-core mode mutation (cluster peer with raw write access to the shared LUN, on-disk corruption, or a separately forged dinode) can then expose the attacker- controlled device number to init_special_inode() without ever showing an unusual i_mode at validation time. System inodes (OCFS2_SYSTEM_FL) legitimately use the bitmap1 and journal1 arms of the same union: allocator inodes encode i_used / i_total in the bitmap1 arm and the journal encodes ij_flags / ij_recovery_generation in the journal1 arm. Those byte sequences are not an i_rdev and a non-zero pattern there is the on-disk norm, not an integrity violation. Restrict the cross- check to non-system inodes; that is the full surface where i_rdev semantics apply and is also the full surface an unprivileged consumer of the volume can see. Following the i_mode canonicalisation in patch 1, S_ISCHR / S_ISBLK covers the whole device-inode space; this check operates correctly on its own, but the canonicalised i_mode makes the predicate exhaustive. Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function= .") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi --- fs/ocfs2/inode.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index fb592bf3e5f31..305e22cc9b1d9 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1533,6 +1533,44 @@ int ocfs2_validate_inode_block(struct super_block *s= b, } } =20 + /* + * id1.dev1.i_rdev is the device-number arm of the id1 union and + * is only meaningful for character and block device inodes. For + * any other regular user-visible file type the on-disk value + * must be zero. ocfs2_populate_inode() currently runs + * + * inode->i_rdev =3D huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev)= ); + * + * unconditionally, before the S_IFMT switch decides whether the + * inode is a special file. As a result, an i_rdev value present + * on a non-device inode is silently published into the in-core + * inode; a subsequent forced re-read or in-core mode mutation + * (cluster peer with raw write access to the shared LUN, + * on-disk corruption, or a separately forged dinode) can then + * expose the attacker-controlled device number to + * init_special_inode() without ever showing an unusual i_mode + * at validation time. + * + * System inodes (OCFS2_SYSTEM_FL) legitimately use the bitmap1 + * and journal1 arms of the same union (allocator i_used / + * i_total counters and the journal ij_flags / + * ij_recovery_generation pair); those bytes are not an i_rdev + * and must not be checked here. Restrict the cross-check to + * non-system inodes, which is the full attacker-controllable + * surface. + */ + if (!(le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL) && + !S_ISCHR(le16_to_cpu(di->i_mode)) && + !S_ISBLK(le16_to_cpu(di->i_mode)) && + di->id1.dev1.i_rdev !=3D 0) { + rc =3D ocfs2_error(sb, + "Invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode), + (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data =3D &di->id2.i_data; =20 --=20 2.53.0 From nobody Mon May 25 06:41:33 2026 Received: from mail-qt1-f179.google.com (mail-qt1-f179.google.com [209.85.160.179]) (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 42DA8361668 for ; Sun, 17 May 2026 11:10:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779016238; cv=none; b=Lq9u8ZpDt7WuGsQ0T/qIq4P9YqPP4n/qx/RezSFkWTE5xd6+CerVWQx1lGeRwevIa3rGmDrVjGr1MGIPG9ufAH3+SErT4Gl3btIuAUz8n48ANWj5MmWoFWKq08Z8XKSVwNT1cGlv2JshehHf8svqmVhuXeFg7pdL7EJRAck0EQQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779016238; c=relaxed/simple; bh=+BSdzo13XZQuvp/wIYzH8e9IUezizz0p0fekzWmT8+E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fidZKnDoKiZnOrlzUJK5Hd65/Mv2JiCnsZ8EbDFJae8rnFTajy/gMzrA9F78fVPIunCWePMt4xB37Hq3xv0geX//JLjqzLV2Z3TW/UlOs9Opvv0wlSZ4uN+5aNb328vUN+qXL2fNcwZU+YapiQTSAdeXCoAqBI2Ea9k+HRjhc1E= 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=ek3zJIdJ; arc=none smtp.client-ip=209.85.160.179 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="ek3zJIdJ" Received: by mail-qt1-f179.google.com with SMTP id d75a77b69052e-50faf8ed9c5so6676571cf.2 for ; Sun, 17 May 2026 04:10:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779016236; x=1779621036; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=lADOBMRjpA9lHPB2reV9gREFqcmYd+mh1OlNIA4DhzU=; b=ek3zJIdJBN4z+eUzM7OvtKijFrdAVHZ5YohPeRvEqw/zILwoTN1QsCNFzOA8QSLl/Z 8myFm0BDto5L7+nIRr9DFDz+cGyVcyOgWS+QyNBJXMvGzcRsnxRfbsBfj31jYGWGzvjG c1fChLSpLiyaQpvMr7VUm28ib8AcoMQ5smSBTJS+wezhl/nQ2iyBH2I88TmvWX1g1pFb kC6iggsZaSkCGnCsG7Oi+2HblSj9gseSzfPw6fQyVE4EPtKyeyBjeSd4BUar35GT5c/e fcFXPWRBFVBUfGxPRXB2OGE0fmDahKTLT1SfRiuIE3PNVT+ZBHwt+85+J0F8O/oGMkG4 B6FQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779016236; x=1779621036; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=lADOBMRjpA9lHPB2reV9gREFqcmYd+mh1OlNIA4DhzU=; b=VVdNdJbd9a0RXYM/PmmE7cw+0h038iCyDnYWW2WEJkEkzwG8g5aBudnAmYty7iXhVe PS0ejiMPiTKQAMIoeC8qtShKCKG3Mf1tzSPjy03brVds6iKgW0Inhn1bOF8dVcpLl660 wWlLzQg3xhCnFqAEeMkQz1tcEkmwVJrsr/spj83XIl+r0+JNgiVDgKa663GR6TEXddLi ShwrvMr6aj9s0HJ76j72XHMaDAMIGfZPe+pVFbXO3icZHst/29NxKp6RV0Yd3pt+Wn/9 qlDRaXyX93SzInQOpJhwJRZ1HY1C9p6c+teWjAudTkQlyDqL0VTZCogQzAZNPlBdDtg4 qEXQ== X-Forwarded-Encrypted: i=1; AFNElJ/0tTNcAad/ISiJzcoXeNclXqwglJosmtYnhVW05lsT6ELQByZuorJ16AdKG8O2jK2PhPZQtG0xkRv7iKQ=@vger.kernel.org X-Gm-Message-State: AOJu0YwoA8u22VtX2zUxQfwheWeDY3iENjVLolgtPwWVKRlALGY3+zfD Jojj8yhjXVTa1IpoJ8d8NSsse/IEAwMW2e/i5UeKYB3tAHNtBTQQftAr X-Gm-Gg: Acq92OEei4vhtMnKwyguX2+w+gUTRHYnaA8sNzDz9nbONPPzSJRr30wstLDhVMPcqWL BoL7WvtrJKhUOYmqWrVYdRgMRmDN8YsyOMOeFcUb2LxyKNlXpX1Bg4tj1UQIeQB3Q45a4SZmR8/ ErvZBmsePx1V40RX8YneBQ5WYf2Wnq43iPH6YF+4nCr5+6NjPTarIAvyVrbs+sX5W6xzSplqL/6 dYhMOI2by9u+0D8yh7nxWlgVG4Pmo/yNrWo3TWiVIGuxA95T+zBA++PtEVy7IuSbtAW/eOxxbPx rxl551NXaE86UStJW6B5n2zPPq1Mv3/M+18zmR1ZyaoyutwUFpAuXM5UzLsgu/sKRkQwBXbHXDF 9IN/066Ryyufdw/JtfCMwzO9OCEJ1EqHM9G6S4aXJLWBz9yTvDRjRLS2agZYXgu9+Gi27oXtfsv dwmPFeipaoHd0yCbf87Ap6zbUAgLKGXXrfD7gQ4gHF40fYvlYx0fy743F6pvkeHWwrq0aV5VENG lHozrSDXl1GYslx6ZA/eailbpWoydZnD3JvOP1UfK7lDYLjHcUFww== X-Received: by 2002:ac8:57ca:0:b0:50d:89bc:f450 with SMTP id d75a77b69052e-5165a001072mr179709841cf.11.1779016236025; Sun, 17 May 2026 04:10:36 -0700 (PDT) Received: from server0.tail6e7dd.ts.net (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-516456df09csm100306651cf.13.2026.05.17.04.10.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 17 May 2026 04:10:35 -0700 (PDT) From: Michael Bommarito To: Joseph Qi , Mark Fasheh , Joel Becker Cc: ZhengYuan Huang , ocfs2-devel@lists.linux.dev, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/3] ocfs2: reject regular files with non-zero i_size and zero i_clusters Date: Sun, 17 May 2026 07:10:14 -0400 Message-ID: <20260517111015.3187935-4-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260517111015.3187935-1-michael.bommarito@gmail.com> References: <20260517111015.3187935-1-michael.bommarito@gmail.com> 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" On a volume mounted WITHOUT OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, a regular file with non-zero i_size, zero i_clusters, and no OCFS2_INLINE_DATA_FL flag is structurally malformed: the extent map declares no allocated clusters yet the size header claims the file has content. ocfs2_populate_inode() copies i_size into the in-core inode and dispatches to ocfs2_aops; subsequent reads or truncates then operate on an inconsistent extent state. This is the shape an attacker who keeps the rest of the extent list intact (to satisfy the inline-data, refcount, chain-list, and per-field validators already in this function) would produce when forging only the inode header to publish a synthetic file size on a victim node. It is also the shape on-disk corruption of the i_clusters field produces. Reject early in the validator. The check is restricted to non-sparse volumes (ocfs2_sparse_alloc() returns false). On non-sparse mounts the allocator path always grows clusters before i_size: ocfs2_extend_file() takes the !sparse branch into ocfs2_extend_no_holes(), which calls ocfs2_extend_allocation() to journal new clusters first, and only then ocfs2_simple_size_update() journals the larger i_size. The truncate path likewise lowers i_size in ocfs2_orphan_for_truncate() and then frees clusters in ocfs2_commit_truncate(), which uses ocfs2_clusters_for_bytes(new_i_size) as its new_highest_cpos: when new_i_size > 0 the floor is at least one cluster, so the on-disk dinode never legitimately exposes a non-inline regular file with i_size > 0 and i_clusters =3D=3D 0 on a non-sparse volume. On sparse-alloc volumes the same shape is legitimate: an ocfs2_extend_file() call goes through ocfs2_zero_extend() + ocfs2_simple_size_update(), which grows i_size on its own without changing i_clusters; a freshly truncate -s 1M of a sparse regular file is therefore on-disk (i_size =3D 1048576, i_clusters =3D 0). The check therefore opts out via ocfs2_sparse_alloc(OCFS2_SB(sb)). System inodes (OCFS2_SYSTEM_FL) carry their own size and cluster invariants validated by the allocator, journal, quota, and truncate-log subsystems; skip them here. The inline-data fast path is filtered separately by its own dedicated branch below: its well-formed case is exactly i_clusters =3D=3D 0 with i_size <=3D id_count. Symlinks legitimately keep i_clusters =3D=3D 0 with non-zero i_size (fast symlinks), so this check is restricted to S_IFREG. Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function= .") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi --- fs/ocfs2/inode.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 305e22cc9b1d9..c63d2ced6b338 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1571,6 +1571,47 @@ int ocfs2_validate_inode_block(struct super_block *s= b, goto bail; } =20 + /* + * On a non-sparse volume, a regular file with non-zero i_size + * and zero i_clusters that is not marked as inline data is + * structurally malformed: the extent map declares no allocated + * clusters yet the size header claims the file has content. + * ocfs2_populate_inode() would still publish i_size to VFS and + * leave the extent state inconsistent for any later read or + * truncate. This is the shape an attacker who keeps the rest + * of the extent list intact (to satisfy the inline-data, + * refcount, chain-list, and per-field validators above) would + * produce when forging only the inode header to publish a + * synthetic file size on a victim node. It is also the shape + * on-disk corruption of the i_clusters field produces. + * + * The check opts out on sparse-alloc volumes, where the + * extend path (ocfs2_extend_file -> ocfs2_zero_extend -> + * ocfs2_simple_size_update) legitimately grows i_size without + * allocating clusters. On non-sparse volumes the equivalent + * path (ocfs2_extend_no_holes) journals clusters first and + * i_size second, and truncate-down floors i_clusters at + * ocfs2_clusters_for_bytes(new_i_size) which is >=3D 1 whenever + * new_i_size > 0, so the rejected shape never appears on disk. + * + * Skip system inodes (OCFS2_SYSTEM_FL) and the inline-data + * fast path (handled below). Symlinks legitimately keep + * i_clusters =3D=3D 0 with non-zero i_size (fast symlinks), so + * restrict to S_IFREG. + */ + if (!ocfs2_sparse_alloc(OCFS2_SB(sb)) && + S_ISREG(le16_to_cpu(di->i_mode)) && + !(le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL) && + !(le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) && + le64_to_cpu(di->i_size) !=3D 0 && + le32_to_cpu(di->i_clusters) =3D=3D 0) { + rc =3D ocfs2_error(sb, + "Invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and= no inline-data flag on non-sparse volume\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data =3D &di->id2.i_data; =20 --=20 2.53.0