While looking at git's handling of invalid objects, I stumbled upon git apparently not caring much about file mode values, except for the following requirements: - must be non-empty (badTree) - must be octal (badTree) - should not be zero-padded (zeroPaddedFilemode) Looking at tree-walk this is obvious from decode_tree_entry: it calls canon_mode, which - checks the S_IFMT exactly, and falls back on 160000 (S_IFGITLINK) for cases other than S_ISREG, S_ISLNK, or S_ISDIR - returns the corresponding S_IF (with the access modes unset) for all cases other than S_ISREG, for which it returns 755 if S_IXUSR is set, otherwise 644 However looking at fsck_tree, it does have a fair amount of code to validate entry modes and a dedicated message id for this (BAD_FILEMODE / badFilemode), it even has a code path for legacy entries with S_IWGRP set (extensively documented under `git fsck --strict`). I guess, over time mode canonicalisation has slowly creeped earlier the tree-parsing code, and (seemingly for several years) it has been occurring before "git fsck" gets tree entry information, so git fsck simply can not see invalid entry modes? Reproduction: - create an empty git repository - create tree for (or with) any possible entry type value - run `git fsck --strict --no-dangling` Expected: all the trees except the 5 with valid filemodes should be flagged Observed: > git fsck --strict --no-dangling Checking object directories: 100% (256/256), done. notice: HEAD points to an unborn branch (master) notice: No default references Script to generate one tree for each filemode between 1 and 777777 (inclusive), can be run from a (non-bare) repository root, beware that the resulting repository takes about 1GB. from hashlib import sha1 from pathlib import Path from zlib import compress def hash_object(type, content): obj = b"%s %d\0%s" % (type.encode(), len(content), content) oid = sha1(obj).hexdigest() e_dir = GIT_ROOT / 'objects' / oid[:2] e_dir.mkdir(exist_ok=True, parents=True) e = e_dir / oid[2:] if not e.exists(): e.write_bytes(compress(obj)) return oid # technically S_IFMT caps out at 200_000 - 1, but get_mode doesn't # actually have a limit MAX_ENTRY = 0o777_777 if __name__ == '__main__': GIT_ROOT = Path.cwd() / '.git' assert GIT_ROOT.is_dir() blob_hex = hash_object('blob', b"") blob_oid = bytes.fromhex(blob_hex) print("empty blob", blob_hex) tree_hex = hash_object("tree", b"100644 empty_blob\0" + blob_oid) tree_oid = bytes.fromhex(tree_hex) print("base tree", tree_hex) for mode in range(1, MAX_ENTRY+1): print(f"\r{mode:06o}/{MAX_ENTRY:o}", end=' ') tree = b"%o x\0%s" % ( mode, tree_oid if (mode & 0o170_000) == 0o040_000 else blob_oid, ) h = hash_object("tree", tree) print("=", h, end='', flush=True)