From mboxrd@z Thu Jan 1 00:00:00 1970 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: 7bit 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 = 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 --- 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 *sb, } } + /* + * 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 = 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 != 0) { + rc = 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 = &di->id2.i_data; -- 2.53.0