The WARN_ON_ONCE() in collect_domain_accesses() can be triggered when trying to link a root mount point. This cannot work in practice because this directory is mounted, but the VFS check is done after the call to security_path_link(). Do not use source directory's d_parent when the source directory is the mount point.
Unified · Split
| @@ -1110,6 +1110,7 @@ static int current_check_refer_path(struct dentry *const old_dentry, · 1109 unmodified lines | |||
| 1110 | bool allow_parent1, allow_parent2; | 1110 | bool allow_parent1, allow_parent2; |
| 1111 | access_mask_t access_request_parent1, access_request_parent2; | 1111 | access_mask_t access_request_parent1, access_request_parent2; |
| 1112 | struct path mnt_dir; | 1112 | struct path mnt_dir; |
| 1113 | struct dentry *old_parent; | ||
| 1113 | layer_mask_t layer_masks_parent1[LANDLOCK_NUM_ACCESS_FS] = {}, | 1114 | layer_mask_t layer_masks_parent1[LANDLOCK_NUM_ACCESS_FS] = {}, |
| 1114 | layer_masks_parent2[LANDLOCK_NUM_ACCESS_FS] = {}; | 1115 | layer_masks_parent2[LANDLOCK_NUM_ACCESS_FS] = {}; |
| 1115 | 1116 | ||
| @@ -1157,9 +1158,17 @@ static int current_check_refer_path(struct dentry *const old_dentry, · 41 unmodified lines | |||
| 1157 | mnt_dir.mnt = new_dir->mnt; | 1158 | mnt_dir.mnt = new_dir->mnt; |
| 1158 | mnt_dir.dentry = new_dir->mnt->mnt_root; | 1159 | mnt_dir.dentry = new_dir->mnt->mnt_root; |
| 1159 | 1160 | ||
| 1161 | /* | ||
| 1162 | * old_dentry may be the root of the common mount point and | ||
| 1163 | * !IS_ROOT(old_dentry) at the same time (e.g. with open_tree() and | ||
| 1164 | * OPEN_TREE_CLONE). We do not need to call dget(old_parent) because | ||
| 1165 | * we keep a reference to old_dentry. | ||
| 1166 | */ | ||
| 1167 | old_parent = (old_dentry == mnt_dir.dentry) ? old_dentry : | ||
| 1168 | old_dentry->d_parent; | ||
| 1169 | |||
| 1160 | /* new_dir->dentry is equal to new_dentry->d_parent */ | 1170 | /* new_dir->dentry is equal to new_dentry->d_parent */ |
| 1161 | allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry, | 1171 | allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry, old_parent, |
| 1162 | old_dentry->d_parent, | ||
| 1163 | &layer_masks_parent1); | 1172 | &layer_masks_parent1); |
| 1164 | allow_parent2 = collect_domain_accesses( | 1173 | allow_parent2 = collect_domain_accesses( |
| 1165 | dom, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2); | 1174 | dom, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2); |