概要

Linux kernel 5.9-rc4以前のXFSでは,metadata validatorのバグにより,有効な拡張属性を持つinodeが破損しているというフラグが立てられる事がある.

影響範囲

RHEL7以降ではXFSをデフォルトで採用している為,影響範囲が非常に広い.

少なくとも手元のVagrant上のcentos/7では発火した.

再現手順

非常に簡単で,拡張属性を1文字にすれば良い.

[vagrant@localhost ~]$ uname -a
Linux localhost.localdomain 3.10.0-1127.el7.x86_64 #1 SMP Tue Mar 31 23:36:51 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
[vagrant@localhost tmp]$ touch test
[vagrant@localhost tmp]$ setfattr -n user.x test
~~ snip ~~
[vagrant@localhost tmp]$ ls
-bash: /usr/bin/ls: Input/output error
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <err.h>

int main(int argc, const char **argv) {
	if (argc<2)
        err(1, "Usage: %s path\n", *argv);

	const char *path = argv[1];
	if (open(path, O_RDWR | O_CREAT)<0)
        err(1, "error when open: %m\n");

	if (setxattr(path, "user.x", NULL, 0, 0)<0)
        err(1, "error when setxattr: %m\n");

	printf("setxattr to %s succeeded!\n", path);
	return 0;
}

再現環境(CentOS7)では拡張属性を設定後,少しするとInput/output errorが発生し,使い物にならない状態となった.

reboot後も,対象のinodeにアクセスしようとすると,

ls: cannot access test: Structure needs cleaning

となる

修正

パッチも非常にシンプルで,

diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index 8623c815164a6..383b08f2ac61f 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -1036,8 +1036,10 @@ xfs_attr_shortform_verify(
 		 * struct xfs_attr_sf_entry has a variable length.
 		 * Check the fixed-offset parts of the structure are
 		 * within the data buffer.
+		 * xfs_attr_sf_entry is defined with a 1-byte variable
+		 * array at the end, so we must subtract that off.
 		 */
-		if (((char *)sfep + sizeof(*sfep)) >= endp)
+		if (((char *)sfep + sizeof(*sfep) - 1) >= endp)
 			return __this_address;

これだけの変更である

今まで誰も気づいていなかったのが不思議だ.

発火のタイミング

修正部分の周辺を読んでみると,対象となる属性を付与したinodeのverify_attrが呼ばれるタイミングで発火するようだ.

sysctlfs.xfs.xfssyncd_centisecs等のパラメタを変更する事で発火のタイミングを調整できる(権限は必要だが).

centisecsなので,1/100秒単位での設定となる.

参考

NVD commit