diff options
| author | Purushottam Choudhary <purushottamchoudhary29@gmail.com> | 2022-01-21 18:37:33 +0530 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2022-02-16 09:48:51 +0000 |
| commit | 40d6918639ce8227215e716551495b90f2197dd7 (patch) | |
| tree | 059ca24a7ed0d0241123874f1e269c970823957e | |
| parent | bbd2561fe9a7b005295183e5ecc74d692203dee4 (diff) | |
| download | poky-40d6918639ce8227215e716551495b90f2197dd7.tar.gz | |
systemd: Fix CVE-2021-3997
Add patches to fix CVE-2021-3997.
Add additional below mentioned patches which are
required to fix CVE:
1. rm-rf-optionally-fsync-after-removing-directory-tree.patch
2. rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch
Link: http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd_245.4-4ubuntu3.15.debian.tar.xz
(From OE-Core rev: b7f79fbf23488b954987dfc4aa867e42bdce7fee)
Signed-off-by: Purushottam Choudhary <purushottam.choudhary@kpit.com>
Signed-off-by: Purushottam Choudhary <purushottamchoudhary29@gmail.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
6 files changed, 790 insertions, 0 deletions
diff --git a/meta/recipes-core/systemd/systemd/CVE-2021-3997-1.patch b/meta/recipes-core/systemd/systemd/CVE-2021-3997-1.patch new file mode 100644 index 0000000000..341976822b --- /dev/null +++ b/meta/recipes-core/systemd/systemd/CVE-2021-3997-1.patch | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | Backport of the following upstream commit: | ||
| 2 | From fbb77e1e55866633c9f064e2b3bcf2b6402d962d Mon Sep 17 00:00:00 2001 | ||
| 3 | From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> | ||
| 4 | Date: Tue, 23 Nov 2021 15:55:45 +0100 | ||
| 5 | Subject: [PATCH 1/3] shared/rm_rf: refactor rm_rf_children_inner() to shorten | ||
| 6 | code a bit | ||
| 7 | |||
| 8 | CVE: CVE-2021-3997 | ||
| 9 | Upstream-Status: Backport [http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd_245.4-4ubuntu3.15.debian.tar.xz] | ||
| 10 | Signed-off-by: Purushottam Choudhary <Purushottam.Choudhary@kpit.com> | ||
| 11 | --- | ||
| 12 | src/basic/rm-rf.c | 27 +++++++++------------------ | ||
| 13 | 1 file changed, 9 insertions(+), 18 deletions(-) | ||
| 14 | |||
| 15 | --- a/src/basic/rm-rf.c | ||
| 16 | +++ b/src/basic/rm-rf.c | ||
| 17 | @@ -34,7 +34,7 @@ | ||
| 18 | const struct stat *root_dev) { | ||
| 19 | |||
| 20 | struct stat st; | ||
| 21 | - int r; | ||
| 22 | + int r, q = 0; | ||
| 23 | |||
| 24 | assert(fd >= 0); | ||
| 25 | assert(fname); | ||
| 26 | @@ -50,7 +50,6 @@ | ||
| 27 | |||
| 28 | if (is_dir) { | ||
| 29 | _cleanup_close_ int subdir_fd = -1; | ||
| 30 | - int q; | ||
| 31 | |||
| 32 | /* if root_dev is set, remove subdirectories only if device is same */ | ||
| 33 | if (root_dev && st.st_dev != root_dev->st_dev) | ||
| 34 | @@ -86,23 +85,15 @@ | ||
| 35 | * again for each directory */ | ||
| 36 | q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); | ||
| 37 | |||
| 38 | - r = unlinkat(fd, fname, AT_REMOVEDIR); | ||
| 39 | - if (r < 0) | ||
| 40 | - return r; | ||
| 41 | - if (q < 0) | ||
| 42 | - return q; | ||
| 43 | - | ||
| 44 | - return 1; | ||
| 45 | - | ||
| 46 | - } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { | ||
| 47 | - r = unlinkat(fd, fname, 0); | ||
| 48 | - if (r < 0) | ||
| 49 | - return r; | ||
| 50 | - | ||
| 51 | - return 1; | ||
| 52 | - } | ||
| 53 | + } else if (flags & REMOVE_ONLY_DIRECTORIES) | ||
| 54 | + return 0; | ||
| 55 | |||
| 56 | - return 0; | ||
| 57 | + r = unlinkat(fd, fname, is_dir ? AT_REMOVEDIR : 0); | ||
| 58 | + if (r < 0) | ||
| 59 | + return r; | ||
| 60 | + if (q < 0) | ||
| 61 | + return q; | ||
| 62 | + return 1; | ||
| 63 | } | ||
| 64 | |||
| 65 | int rm_rf_children( | ||
diff --git a/meta/recipes-core/systemd/systemd/CVE-2021-3997-2.patch b/meta/recipes-core/systemd/systemd/CVE-2021-3997-2.patch new file mode 100644 index 0000000000..066e10fbbc --- /dev/null +++ b/meta/recipes-core/systemd/systemd/CVE-2021-3997-2.patch | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | Backport of the following upstream commit: | ||
| 2 | From bd0127daaaae009ade053718f7d2f297aee4acaf Mon Sep 17 00:00:00 2001 | ||
| 3 | From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> | ||
| 4 | Date: Tue, 23 Nov 2021 16:56:42 +0100 | ||
| 5 | Subject: [PATCH 2/3] shared/rm_rf: refactor rm_rf() to shorten code a bit | ||
| 6 | |||
| 7 | CVE: CVE-2021-3997 | ||
| 8 | Upstream-Status: Backport [http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd_245.4-4ubuntu3.15.debian.tar.xz] | ||
| 9 | Signed-off-by: Purushottam Choudhary <Purushottam.Choudhary@kpit.com> | ||
| 10 | --- | ||
| 11 | src/basic/rm-rf.c | 53 ++++++++++++++++++++-------------------------- | ||
| 12 | 1 file changed, 23 insertions(+), 30 deletions(-) | ||
| 13 | |||
| 14 | --- a/src/basic/rm-rf.c | ||
| 15 | +++ b/src/basic/rm-rf.c | ||
| 16 | @@ -159,7 +159,7 @@ | ||
| 17 | } | ||
| 18 | |||
| 19 | int rm_rf(const char *path, RemoveFlags flags) { | ||
| 20 | - int fd, r; | ||
| 21 | + int fd, r, q = 0; | ||
| 22 | |||
| 23 | assert(path); | ||
| 24 | |||
| 25 | @@ -191,49 +191,47 @@ | ||
| 26 | } | ||
| 27 | |||
| 28 | fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); | ||
| 29 | - if (fd < 0) { | ||
| 30 | + if (fd >= 0) { | ||
| 31 | + /* We have a dir */ | ||
| 32 | + r = rm_rf_children(fd, flags, NULL); | ||
| 33 | + | ||
| 34 | + if (FLAGS_SET(flags, REMOVE_ROOT)) { | ||
| 35 | + q = rmdir(path); | ||
| 36 | + if (q < 0) | ||
| 37 | + q = -errno; | ||
| 38 | + } | ||
| 39 | + } else { | ||
| 40 | if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT) | ||
| 41 | return 0; | ||
| 42 | |||
| 43 | if (!IN_SET(errno, ENOTDIR, ELOOP)) | ||
| 44 | return -errno; | ||
| 45 | |||
| 46 | - if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES)) | ||
| 47 | + if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES) || !FLAGS_SET(flags, REMOVE_ROOT)) | ||
| 48 | return 0; | ||
| 49 | |||
| 50 | - if (FLAGS_SET(flags, REMOVE_ROOT)) { | ||
| 51 | - | ||
| 52 | - if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) { | ||
| 53 | - struct statfs s; | ||
| 54 | - | ||
| 55 | - if (statfs(path, &s) < 0) | ||
| 56 | - return -errno; | ||
| 57 | - if (is_physical_fs(&s)) | ||
| 58 | - return log_error_errno(SYNTHETIC_ERRNO(EPERM), | ||
| 59 | - "Attempted to remove files from a disk file system under \"%s\", refusing.", | ||
| 60 | - path); | ||
| 61 | - } | ||
| 62 | - | ||
| 63 | - if (unlink(path) < 0) { | ||
| 64 | - if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT) | ||
| 65 | - return 0; | ||
| 66 | + if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) { | ||
| 67 | + struct statfs s; | ||
| 68 | |||
| 69 | + if (statfs(path, &s) < 0) | ||
| 70 | return -errno; | ||
| 71 | - } | ||
| 72 | + if (is_physical_fs(&s)) | ||
| 73 | + return log_error_errno(SYNTHETIC_ERRNO(EPERM), | ||
| 74 | + "Attempted to remove files from a disk file system under \"%s\", refusing.", | ||
| 75 | + path); | ||
| 76 | } | ||
| 77 | |||
| 78 | - return 0; | ||
| 79 | + r = 0; | ||
| 80 | + q = unlink(path); | ||
| 81 | + if (q < 0) | ||
| 82 | + q = -errno; | ||
| 83 | } | ||
| 84 | |||
| 85 | - r = rm_rf_children(fd, flags, NULL); | ||
| 86 | - | ||
| 87 | - if (FLAGS_SET(flags, REMOVE_ROOT) && | ||
| 88 | - rmdir(path) < 0 && | ||
| 89 | - r >= 0 && | ||
| 90 | - (!FLAGS_SET(flags, REMOVE_MISSING_OK) || errno != ENOENT)) | ||
| 91 | - r = -errno; | ||
| 92 | - | ||
| 93 | - return r; | ||
| 94 | + if (r < 0) | ||
| 95 | + return r; | ||
| 96 | + if (q < 0 && (q != -ENOENT || !FLAGS_SET(flags, REMOVE_MISSING_OK))) | ||
| 97 | + return q; | ||
| 98 | + return 0; | ||
| 99 | } | ||
| 100 | |||
| 101 | int rm_rf_child(int fd, const char *name, RemoveFlags flags) { | ||
diff --git a/meta/recipes-core/systemd/systemd/CVE-2021-3997-3.patch b/meta/recipes-core/systemd/systemd/CVE-2021-3997-3.patch new file mode 100644 index 0000000000..c96b8d9a6e --- /dev/null +++ b/meta/recipes-core/systemd/systemd/CVE-2021-3997-3.patch | |||
| @@ -0,0 +1,266 @@ | |||
| 1 | Backport of the following upstream commit: | ||
| 2 | From bef8e8e577368697b2e6f85183b1dbc99e0e520f Mon Sep 17 00:00:00 2001 | ||
| 3 | From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl> | ||
| 4 | Date: Tue, 30 Nov 2021 22:29:05 +0100 | ||
| 5 | Subject: [PATCH 3/3] shared/rm-rf: loop over nested directories instead of | ||
| 6 | instead of recursing | ||
| 7 | |||
| 8 | To remove directory structures, we need to remove the innermost items first, | ||
| 9 | and then recursively remove higher-level directories. We would recursively | ||
| 10 | descend into directories and invoke rm_rf_children and rm_rm_children_inner. | ||
| 11 | This is problematic when too many directories are nested. | ||
| 12 | |||
| 13 | Instead, let's create a "TODO" queue. In the the queue, for each level we | ||
| 14 | hold the DIR* object we were working on, and the name of the directory. This | ||
| 15 | allows us to leave a partially-processed directory, and restart the removal | ||
| 16 | loop one level down. When done with the inner directory, we use the name to | ||
| 17 | unlinkat() it from the parent, and proceed with the removal of other items. | ||
| 18 | |||
| 19 | Because the nesting is increased by one level, it is best to view this patch | ||
| 20 | with -b/--ignore-space-change. | ||
| 21 | |||
| 22 | This fixes CVE-2021-3997, https://bugzilla.redhat.com/show_bug.cgi?id=2024639. | ||
| 23 | The issue was reported and patches reviewed by Qualys Team. | ||
| 24 | Mauro Matteo Cascella and Riccardo Schirone from Red Hat handled the disclosure. | ||
| 25 | |||
| 26 | CVE: CVE-2021-3997 | ||
| 27 | Upstream-Status: Backport [http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd_245.4-4ubuntu3.15.debian.tar.xz] | ||
| 28 | Signed-off-by: Purushottam Choudhary <Purushottam.Choudhary@kpit.com> | ||
| 29 | --- | ||
| 30 | src/basic/rm-rf.c | 161 +++++++++++++++++++++++++++++++-------------- | ||
| 31 | 1 file changed, 113 insertions(+), 48 deletions(-) | ||
| 32 | |||
| 33 | --- a/src/basic/rm-rf.c | ||
| 34 | +++ b/src/basic/rm-rf.c | ||
| 35 | @@ -26,12 +26,13 @@ | ||
| 36 | return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs); | ||
| 37 | } | ||
| 38 | |||
| 39 | -static int rm_rf_children_inner( | ||
| 40 | +static int rm_rf_inner_child( | ||
| 41 | int fd, | ||
| 42 | const char *fname, | ||
| 43 | int is_dir, | ||
| 44 | RemoveFlags flags, | ||
| 45 | - const struct stat *root_dev) { | ||
| 46 | + const struct stat *root_dev, | ||
| 47 | + bool allow_recursion) { | ||
| 48 | |||
| 49 | struct stat st; | ||
| 50 | int r, q = 0; | ||
| 51 | @@ -49,9 +50,7 @@ | ||
| 52 | } | ||
| 53 | |||
| 54 | if (is_dir) { | ||
| 55 | - _cleanup_close_ int subdir_fd = -1; | ||
| 56 | - | ||
| 57 | - /* if root_dev is set, remove subdirectories only if device is same */ | ||
| 58 | + /* If root_dev is set, remove subdirectories only if device is same */ | ||
| 59 | if (root_dev && st.st_dev != root_dev->st_dev) | ||
| 60 | return 0; | ||
| 61 | |||
| 62 | @@ -63,7 +62,6 @@ | ||
| 63 | return 0; | ||
| 64 | |||
| 65 | if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { | ||
| 66 | - | ||
| 67 | /* This could be a subvolume, try to remove it */ | ||
| 68 | |||
| 69 | r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); | ||
| 70 | @@ -77,13 +75,16 @@ | ||
| 71 | return 1; | ||
| 72 | } | ||
| 73 | |||
| 74 | - subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); | ||
| 75 | + if (!allow_recursion) | ||
| 76 | + return -EISDIR; | ||
| 77 | + | ||
| 78 | + int subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); | ||
| 79 | if (subdir_fd < 0) | ||
| 80 | return -errno; | ||
| 81 | |||
| 82 | /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type | ||
| 83 | * again for each directory */ | ||
| 84 | - q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); | ||
| 85 | + q = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev); | ||
| 86 | |||
| 87 | } else if (flags & REMOVE_ONLY_DIRECTORIES) | ||
| 88 | return 0; | ||
| 89 | @@ -96,64 +97,128 @@ | ||
| 90 | return 1; | ||
| 91 | } | ||
| 92 | |||
| 93 | +typedef struct TodoEntry { | ||
| 94 | + DIR *dir; /* A directory that we were operating on. */ | ||
| 95 | + char *dirname; /* The filename of that directory itself. */ | ||
| 96 | +} TodoEntry; | ||
| 97 | + | ||
| 98 | +static void free_todo_entries(TodoEntry **todos) { | ||
| 99 | + for (TodoEntry *x = *todos; x && x->dir; x++) { | ||
| 100 | + closedir(x->dir); | ||
| 101 | + free(x->dirname); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + freep(todos); | ||
| 105 | +} | ||
| 106 | + | ||
| 107 | int rm_rf_children( | ||
| 108 | int fd, | ||
| 109 | RemoveFlags flags, | ||
| 110 | const struct stat *root_dev) { | ||
| 111 | |||
| 112 | - _cleanup_closedir_ DIR *d = NULL; | ||
| 113 | - struct dirent *de; | ||
| 114 | + _cleanup_(free_todo_entries) TodoEntry *todos = NULL; | ||
| 115 | + size_t n_todo = 0, allocated = 0; | ||
| 116 | + _cleanup_free_ char *dirname = NULL; /* Set when we are recursing and want to delete ourselves */ | ||
| 117 | int ret = 0, r; | ||
| 118 | |||
| 119 | - assert(fd >= 0); | ||
| 120 | + /* Return the first error we run into, but nevertheless try to go on. | ||
| 121 | + * The passed fd is closed in all cases, including on failure. */ | ||
| 122 | |||
| 123 | - /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed | ||
| 124 | - * fd, in all cases, including on failure. */ | ||
| 125 | + for (;;) { /* This loop corresponds to the directory nesting level. */ | ||
| 126 | + _cleanup_closedir_ DIR *d = NULL; | ||
| 127 | + struct dirent *de; | ||
| 128 | + | ||
| 129 | + if (n_todo > 0) { | ||
| 130 | + /* We know that we are in recursion here, because n_todo is set. | ||
| 131 | + * We need to remove the inner directory we were operating on. */ | ||
| 132 | + assert(dirname); | ||
| 133 | + r = unlinkat(dirfd(todos[n_todo-1].dir), dirname, AT_REMOVEDIR); | ||
| 134 | + if (r < 0 && r != -ENOENT && ret == 0) | ||
| 135 | + ret = r; | ||
| 136 | + dirname = mfree(dirname); | ||
| 137 | + | ||
| 138 | + /* And now let's back out one level up */ | ||
| 139 | + n_todo --; | ||
| 140 | + d = TAKE_PTR(todos[n_todo].dir); | ||
| 141 | + dirname = TAKE_PTR(todos[n_todo].dirname); | ||
| 142 | + | ||
| 143 | + assert(d); | ||
| 144 | + fd = dirfd(d); /* Retrieve the file descriptor from the DIR object */ | ||
| 145 | + assert(fd >= 0); | ||
| 146 | + } else { | ||
| 147 | + next_fd: | ||
| 148 | + assert(fd >= 0); | ||
| 149 | + d = fdopendir(fd); | ||
| 150 | + if (!d) { | ||
| 151 | + safe_close(fd); | ||
| 152 | + return -errno; | ||
| 153 | + } | ||
| 154 | + fd = dirfd(d); /* We donated the fd to fdopendir(). Let's make sure we sure we have | ||
| 155 | + * the right descriptor even if it were to internally invalidate the | ||
| 156 | + * one we passed. */ | ||
| 157 | + | ||
| 158 | + if (!(flags & REMOVE_PHYSICAL)) { | ||
| 159 | + struct statfs sfs; | ||
| 160 | + | ||
| 161 | + if (fstatfs(fd, &sfs) < 0) | ||
| 162 | + return -errno; | ||
| 163 | + | ||
| 164 | + if (is_physical_fs(&sfs)) { | ||
| 165 | + /* We refuse to clean physical file systems with this call, unless | ||
| 166 | + * explicitly requested. This is extra paranoia just to be sure we | ||
| 167 | + * never ever remove non-state data. */ | ||
| 168 | + | ||
| 169 | + _cleanup_free_ char *path = NULL; | ||
| 170 | + | ||
| 171 | + (void) fd_get_path(fd, &path); | ||
| 172 | + return log_error_errno(SYNTHETIC_ERRNO(EPERM), | ||
| 173 | + "Attempted to remove disk file system under \"%s\", and we can't allow that.", | ||
| 174 | + strna(path)); | ||
| 175 | + } | ||
| 176 | + } | ||
| 177 | + } | ||
| 178 | |||
| 179 | - d = fdopendir(fd); | ||
| 180 | - if (!d) { | ||
| 181 | - safe_close(fd); | ||
| 182 | - return -errno; | ||
| 183 | - } | ||
| 184 | + FOREACH_DIRENT_ALL(de, d, return -errno) { | ||
| 185 | + int is_dir; | ||
| 186 | |||
| 187 | - if (!(flags & REMOVE_PHYSICAL)) { | ||
| 188 | - struct statfs sfs; | ||
| 189 | + if (dot_or_dot_dot(de->d_name)) | ||
| 190 | + continue; | ||
| 191 | |||
| 192 | - if (fstatfs(dirfd(d), &sfs) < 0) | ||
| 193 | - return -errno; | ||
| 194 | - } | ||
| 195 | + is_dir = de->d_type == DT_UNKNOWN ? -1 : de->d_type == DT_DIR; | ||
| 196 | |||
| 197 | - if (is_physical_fs(&sfs)) { | ||
| 198 | - /* We refuse to clean physical file systems with this call, unless explicitly | ||
| 199 | - * requested. This is extra paranoia just to be sure we never ever remove non-state | ||
| 200 | - * data. */ | ||
| 201 | - | ||
| 202 | - _cleanup_free_ char *path = NULL; | ||
| 203 | - | ||
| 204 | - (void) fd_get_path(fd, &path); | ||
| 205 | - return log_error_errno(SYNTHETIC_ERRNO(EPERM), | ||
| 206 | - "Attempted to remove disk file system under \"%s\", and we can't allow that.", | ||
| 207 | - strna(path)); | ||
| 208 | - } | ||
| 209 | - } | ||
| 210 | + r = rm_rf_inner_child(fd, de->d_name, is_dir, flags, root_dev, false); | ||
| 211 | + if (r == -EISDIR) { | ||
| 212 | + /* Push the current working state onto the todo list */ | ||
| 213 | |||
| 214 | - FOREACH_DIRENT_ALL(de, d, return -errno) { | ||
| 215 | - int is_dir; | ||
| 216 | + if (!GREEDY_REALLOC0(todos, allocated, n_todo + 2)) | ||
| 217 | + return log_oom(); | ||
| 218 | |||
| 219 | - if (dot_or_dot_dot(de->d_name)) | ||
| 220 | - continue; | ||
| 221 | + _cleanup_free_ char *newdirname = strdup(de->d_name); | ||
| 222 | + if (!newdirname) | ||
| 223 | + return log_oom(); | ||
| 224 | |||
| 225 | - is_dir = | ||
| 226 | - de->d_type == DT_UNKNOWN ? -1 : | ||
| 227 | - de->d_type == DT_DIR; | ||
| 228 | - | ||
| 229 | - r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev); | ||
| 230 | - if (r < 0 && r != -ENOENT && ret == 0) | ||
| 231 | - ret = r; | ||
| 232 | - } | ||
| 233 | + int newfd = openat(fd, de->d_name, | ||
| 234 | + O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); | ||
| 235 | + if (newfd >= 0) { | ||
| 236 | + todos[n_todo++] = (TodoEntry) { TAKE_PTR(d), TAKE_PTR(dirname) }; | ||
| 237 | + fd = newfd; | ||
| 238 | + dirname = TAKE_PTR(newdirname); | ||
| 239 | + | ||
| 240 | + goto next_fd; | ||
| 241 | |||
| 242 | - if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(dirfd(d)) < 0 && ret >= 0) | ||
| 243 | - ret = -errno; | ||
| 244 | + } else if (errno != -ENOENT && ret == 0) | ||
| 245 | + ret = -errno; | ||
| 246 | + | ||
| 247 | + } else if (r < 0 && r != -ENOENT && ret == 0) | ||
| 248 | + ret = r; | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | + if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(fd) < 0 && ret >= 0) | ||
| 252 | + ret = -errno; | ||
| 253 | + | ||
| 254 | + if (n_todo == 0) | ||
| 255 | + break; | ||
| 256 | + } | ||
| 257 | |||
| 258 | return ret; | ||
| 259 | } | ||
| 260 | @@ -250,5 +315,5 @@ | ||
| 261 | if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME)) | ||
| 262 | return -EINVAL; | ||
| 263 | |||
| 264 | - return rm_rf_children_inner(fd, name, -1, flags, NULL); | ||
| 265 | + return rm_rf_inner_child(fd, name, -1, flags, NULL, true); | ||
| 266 | } | ||
diff --git a/meta/recipes-core/systemd/systemd/rm-rf-optionally-fsync-after-removing-directory-tree.patch b/meta/recipes-core/systemd/systemd/rm-rf-optionally-fsync-after-removing-directory-tree.patch new file mode 100644 index 0000000000..b860da008c --- /dev/null +++ b/meta/recipes-core/systemd/systemd/rm-rf-optionally-fsync-after-removing-directory-tree.patch | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | Backport of the following upstream commit: | ||
| 2 | From bdfe7ada0d4d66e6d6e65f2822acbb1ec230f9c2 Mon Sep 17 00:00:00 2001 | ||
| 3 | From: Lennart Poettering <lennart@poettering.net> | ||
| 4 | Date: Tue, 5 Oct 2021 10:32:56 +0200 | ||
| 5 | Subject: [PATCH] rm-rf: optionally fsync() after removing directory tree | ||
| 6 | |||
| 7 | Upstream-Status: Backport [http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd_245.4-4ubuntu3.15.debian.tar.xz] | ||
| 8 | Signed-off-by: Purushottam Choudhary <Purushottam.Choudhary@kpit.com> | ||
| 9 | --- | ||
| 10 | src/basic/rm-rf.c | 3 +++ | ||
| 11 | src/basic/rm-rf.h | 1 + | ||
| 12 | 2 files changed, 4 insertions(+) | ||
| 13 | |||
| 14 | --- a/src/basic/rm-rf.c | ||
| 15 | +++ b/src/basic/rm-rf.c | ||
| 16 | @@ -161,6 +161,9 @@ | ||
| 17 | ret = r; | ||
| 18 | } | ||
| 19 | |||
| 20 | + if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(dirfd(d)) < 0 && ret >= 0) | ||
| 21 | + ret = -errno; | ||
| 22 | + | ||
| 23 | return ret; | ||
| 24 | } | ||
| 25 | |||
| 26 | --- a/src/basic/rm-rf.h | ||
| 27 | +++ b/src/basic/rm-rf.h | ||
| 28 | @@ -11,6 +11,7 @@ | ||
| 29 | REMOVE_PHYSICAL = 1 << 2, /* If not set, only removes files on tmpfs, never physical file systems */ | ||
| 30 | REMOVE_SUBVOLUME = 1 << 3, /* Drop btrfs subvolumes in the tree too */ | ||
| 31 | REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */ | ||
| 32 | + REMOVE_SYNCFS = 1 << 7, /* syncfs() the root of the specified directory after removing everything in it */ | ||
| 33 | } RemoveFlags; | ||
| 34 | |||
| 35 | int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev); | ||
diff --git a/meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch b/meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch new file mode 100644 index 0000000000..f80e6433c6 --- /dev/null +++ b/meta/recipes-core/systemd/systemd/rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch | |||
| @@ -0,0 +1,318 @@ | |||
| 1 | Backport of the following upstream commit: | ||
| 2 | From 96906b22417c65d70933976e0ee920c70c9113a4 Mon Sep 17 00:00:00 2001 | ||
| 3 | From: Lennart Poettering <lennart@poettering.net> | ||
| 4 | Date: Tue, 26 Jan 2021 16:30:06 +0100 | ||
| 5 | Subject: [PATCH] rm-rf: refactor rm_rf_children(), split out body of directory | ||
| 6 | iteration loop | ||
| 7 | |||
| 8 | This splits out rm_rf_children_inner() as body of the loop. We can use | ||
| 9 | that to implement rm_rf_child() for deleting one specific entry in a | ||
| 10 | directory. | ||
| 11 | |||
| 12 | Upstream-Status: Backport [http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd_245.4-4ubuntu3.15.debian.tar.xz] | ||
| 13 | Signed-off-by: Purushottam Choudhary <Purushottam.Choudhary@kpit.com> | ||
| 14 | --- | ||
| 15 | src/basic/rm-rf.c | 223 ++++++++++++++++++++++++++------------------- | ||
| 16 | src/basic/rm-rf.h | 3 +- | ||
| 17 | 2 files changed, 131 insertions(+), 95 deletions(-) | ||
| 18 | |||
| 19 | --- a/src/basic/rm-rf.c | ||
| 20 | +++ b/src/basic/rm-rf.c | ||
| 21 | @@ -19,138 +19,153 @@ | ||
| 22 | #include "stat-util.h" | ||
| 23 | #include "string-util.h" | ||
| 24 | |||
| 25 | +/* We treat tmpfs/ramfs + cgroupfs as non-physical file sytems. cgroupfs is similar to tmpfs in a way after | ||
| 26 | + * all: we can create arbitrary directory hierarchies in it, and hence can also use rm_rf() on it to remove | ||
| 27 | + * those again. */ | ||
| 28 | static bool is_physical_fs(const struct statfs *sfs) { | ||
| 29 | return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs); | ||
| 30 | } | ||
| 31 | |||
| 32 | -int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { | ||
| 33 | +static int rm_rf_children_inner( | ||
| 34 | + int fd, | ||
| 35 | + const char *fname, | ||
| 36 | + int is_dir, | ||
| 37 | + RemoveFlags flags, | ||
| 38 | + const struct stat *root_dev) { | ||
| 39 | + | ||
| 40 | + struct stat st; | ||
| 41 | + int r; | ||
| 42 | + | ||
| 43 | + assert(fd >= 0); | ||
| 44 | + assert(fname); | ||
| 45 | + | ||
| 46 | + if (is_dir < 0 || (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) { | ||
| 47 | + | ||
| 48 | + r = fstatat(fd, fname, &st, AT_SYMLINK_NOFOLLOW); | ||
| 49 | + if (r < 0) | ||
| 50 | + return r; | ||
| 51 | + | ||
| 52 | + is_dir = S_ISDIR(st.st_mode); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + if (is_dir) { | ||
| 56 | + _cleanup_close_ int subdir_fd = -1; | ||
| 57 | + int q; | ||
| 58 | + | ||
| 59 | + /* if root_dev is set, remove subdirectories only if device is same */ | ||
| 60 | + if (root_dev && st.st_dev != root_dev->st_dev) | ||
| 61 | + return 0; | ||
| 62 | + | ||
| 63 | + /* Stop at mount points */ | ||
| 64 | + r = fd_is_mount_point(fd, fname, 0); | ||
| 65 | + if (r < 0) | ||
| 66 | + return r; | ||
| 67 | + if (r > 0) | ||
| 68 | + return 0; | ||
| 69 | + | ||
| 70 | + if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { | ||
| 71 | + | ||
| 72 | + /* This could be a subvolume, try to remove it */ | ||
| 73 | + | ||
| 74 | + r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); | ||
| 75 | + if (r < 0) { | ||
| 76 | + if (!IN_SET(r, -ENOTTY, -EINVAL)) | ||
| 77 | + return r; | ||
| 78 | + | ||
| 79 | + /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */ | ||
| 80 | + } else | ||
| 81 | + /* It was a subvolume, done. */ | ||
| 82 | + return 1; | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); | ||
| 86 | + if (subdir_fd < 0) | ||
| 87 | + return -errno; | ||
| 88 | + | ||
| 89 | + /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type | ||
| 90 | + * again for each directory */ | ||
| 91 | + q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); | ||
| 92 | + | ||
| 93 | + r = unlinkat(fd, fname, AT_REMOVEDIR); | ||
| 94 | + if (r < 0) | ||
| 95 | + return r; | ||
| 96 | + if (q < 0) | ||
| 97 | + return q; | ||
| 98 | + | ||
| 99 | + return 1; | ||
| 100 | + | ||
| 101 | + } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { | ||
| 102 | + r = unlinkat(fd, fname, 0); | ||
| 103 | + if (r < 0) | ||
| 104 | + return r; | ||
| 105 | + | ||
| 106 | + return 1; | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + return 0; | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +int rm_rf_children( | ||
| 113 | + int fd, | ||
| 114 | + RemoveFlags flags, | ||
| 115 | + const struct stat *root_dev) { | ||
| 116 | + | ||
| 117 | _cleanup_closedir_ DIR *d = NULL; | ||
| 118 | struct dirent *de; | ||
| 119 | int ret = 0, r; | ||
| 120 | - struct statfs sfs; | ||
| 121 | |||
| 122 | assert(fd >= 0); | ||
| 123 | |||
| 124 | /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed | ||
| 125 | - * fd, in all cases, including on failure.. */ | ||
| 126 | + * fd, in all cases, including on failure. */ | ||
| 127 | + | ||
| 128 | + d = fdopendir(fd); | ||
| 129 | + if (!d) { | ||
| 130 | + safe_close(fd); | ||
| 131 | + return -errno; | ||
| 132 | + } | ||
| 133 | |||
| 134 | if (!(flags & REMOVE_PHYSICAL)) { | ||
| 135 | + struct statfs sfs; | ||
| 136 | |||
| 137 | - r = fstatfs(fd, &sfs); | ||
| 138 | - if (r < 0) { | ||
| 139 | - safe_close(fd); | ||
| 140 | + if (fstatfs(dirfd(d), &sfs) < 0) | ||
| 141 | return -errno; | ||
| 142 | } | ||
| 143 | |||
| 144 | if (is_physical_fs(&sfs)) { | ||
| 145 | - /* We refuse to clean physical file systems with this call, | ||
| 146 | - * unless explicitly requested. This is extra paranoia just | ||
| 147 | - * to be sure we never ever remove non-state data. */ | ||
| 148 | + /* We refuse to clean physical file systems with this call, unless explicitly | ||
| 149 | + * requested. This is extra paranoia just to be sure we never ever remove non-state | ||
| 150 | + * data. */ | ||
| 151 | + | ||
| 152 | _cleanup_free_ char *path = NULL; | ||
| 153 | |||
| 154 | (void) fd_get_path(fd, &path); | ||
| 155 | - log_error("Attempted to remove disk file system under \"%s\", and we can't allow that.", | ||
| 156 | - strna(path)); | ||
| 157 | - | ||
| 158 | - safe_close(fd); | ||
| 159 | - return -EPERM; | ||
| 160 | + return log_error_errno(SYNTHETIC_ERRNO(EPERM), | ||
| 161 | + "Attempted to remove disk file system under \"%s\", and we can't allow that.", | ||
| 162 | + strna(path)); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | - d = fdopendir(fd); | ||
| 167 | - if (!d) { | ||
| 168 | - safe_close(fd); | ||
| 169 | - return errno == ENOENT ? 0 : -errno; | ||
| 170 | - } | ||
| 171 | - | ||
| 172 | FOREACH_DIRENT_ALL(de, d, return -errno) { | ||
| 173 | - bool is_dir; | ||
| 174 | - struct stat st; | ||
| 175 | + int is_dir; | ||
| 176 | |||
| 177 | if (dot_or_dot_dot(de->d_name)) | ||
| 178 | continue; | ||
| 179 | |||
| 180 | - if (de->d_type == DT_UNKNOWN || | ||
| 181 | - (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) { | ||
| 182 | - if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { | ||
| 183 | - if (ret == 0 && errno != ENOENT) | ||
| 184 | - ret = -errno; | ||
| 185 | - continue; | ||
| 186 | - } | ||
| 187 | - | ||
| 188 | - is_dir = S_ISDIR(st.st_mode); | ||
| 189 | - } else | ||
| 190 | - is_dir = de->d_type == DT_DIR; | ||
| 191 | - | ||
| 192 | - if (is_dir) { | ||
| 193 | - _cleanup_close_ int subdir_fd = -1; | ||
| 194 | - | ||
| 195 | - /* if root_dev is set, remove subdirectories only if device is same */ | ||
| 196 | - if (root_dev && st.st_dev != root_dev->st_dev) | ||
| 197 | - continue; | ||
| 198 | - | ||
| 199 | - subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); | ||
| 200 | - if (subdir_fd < 0) { | ||
| 201 | - if (ret == 0 && errno != ENOENT) | ||
| 202 | - ret = -errno; | ||
| 203 | - continue; | ||
| 204 | - } | ||
| 205 | - | ||
| 206 | - /* Stop at mount points */ | ||
| 207 | - r = fd_is_mount_point(fd, de->d_name, 0); | ||
| 208 | - if (r < 0) { | ||
| 209 | - if (ret == 0 && r != -ENOENT) | ||
| 210 | - ret = r; | ||
| 211 | - | ||
| 212 | - continue; | ||
| 213 | - } | ||
| 214 | - if (r > 0) | ||
| 215 | - continue; | ||
| 216 | - | ||
| 217 | - if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { | ||
| 218 | - | ||
| 219 | - /* This could be a subvolume, try to remove it */ | ||
| 220 | - | ||
| 221 | - r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); | ||
| 222 | - if (r < 0) { | ||
| 223 | - if (!IN_SET(r, -ENOTTY, -EINVAL)) { | ||
| 224 | - if (ret == 0) | ||
| 225 | - ret = r; | ||
| 226 | - | ||
| 227 | - continue; | ||
| 228 | - } | ||
| 229 | - | ||
| 230 | - /* ENOTTY, then it wasn't a btrfs subvolume, continue below. */ | ||
| 231 | - } else | ||
| 232 | - /* It was a subvolume, continue. */ | ||
| 233 | - continue; | ||
| 234 | - } | ||
| 235 | - | ||
| 236 | - /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file | ||
| 237 | - * system type again for each directory */ | ||
| 238 | - r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev); | ||
| 239 | - if (r < 0 && ret == 0) | ||
| 240 | - ret = r; | ||
| 241 | - | ||
| 242 | - if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { | ||
| 243 | - if (ret == 0 && errno != ENOENT) | ||
| 244 | - ret = -errno; | ||
| 245 | - } | ||
| 246 | - | ||
| 247 | - } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { | ||
| 248 | - | ||
| 249 | - if (unlinkat(fd, de->d_name, 0) < 0) { | ||
| 250 | - if (ret == 0 && errno != ENOENT) | ||
| 251 | - ret = -errno; | ||
| 252 | - } | ||
| 253 | - } | ||
| 254 | + is_dir = | ||
| 255 | + de->d_type == DT_UNKNOWN ? -1 : | ||
| 256 | + de->d_type == DT_DIR; | ||
| 257 | + | ||
| 258 | + r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev); | ||
| 259 | + if (r < 0 && r != -ENOENT && ret == 0) | ||
| 260 | + ret = r; | ||
| 261 | } | ||
| 262 | + | ||
| 263 | return ret; | ||
| 264 | } | ||
| 265 | |||
| 266 | int rm_rf(const char *path, RemoveFlags flags) { | ||
| 267 | int fd, r; | ||
| 268 | - struct statfs s; | ||
| 269 | |||
| 270 | assert(path); | ||
| 271 | |||
| 272 | @@ -195,9 +210,10 @@ | ||
| 273 | if (FLAGS_SET(flags, REMOVE_ROOT)) { | ||
| 274 | |||
| 275 | if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) { | ||
| 276 | + struct statfs s; | ||
| 277 | + | ||
| 278 | if (statfs(path, &s) < 0) | ||
| 279 | return -errno; | ||
| 280 | - | ||
| 281 | if (is_physical_fs(&s)) | ||
| 282 | return log_error_errno(SYNTHETIC_ERRNO(EPERM), | ||
| 283 | "Attempted to remove files from a disk file system under \"%s\", refusing.", | ||
| 284 | @@ -225,3 +241,22 @@ | ||
| 285 | |||
| 286 | return r; | ||
| 287 | } | ||
| 288 | + | ||
| 289 | +int rm_rf_child(int fd, const char *name, RemoveFlags flags) { | ||
| 290 | + | ||
| 291 | + /* Removes one specific child of the specified directory */ | ||
| 292 | + | ||
| 293 | + if (fd < 0) | ||
| 294 | + return -EBADF; | ||
| 295 | + | ||
| 296 | + if (!filename_is_valid(name)) | ||
| 297 | + return -EINVAL; | ||
| 298 | + | ||
| 299 | + if ((flags & (REMOVE_ROOT|REMOVE_MISSING_OK)) != 0) /* Doesn't really make sense here, we are not supposed to remove 'fd' anyway */ | ||
| 300 | + return -EINVAL; | ||
| 301 | + | ||
| 302 | + if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME)) | ||
| 303 | + return -EINVAL; | ||
| 304 | + | ||
| 305 | + return rm_rf_children_inner(fd, name, -1, flags, NULL); | ||
| 306 | +} | ||
| 307 | --- a/src/basic/rm-rf.h | ||
| 308 | +++ b/src/basic/rm-rf.h | ||
| 309 | @@ -13,7 +13,8 @@ | ||
| 310 | REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */ | ||
| 311 | } RemoveFlags; | ||
| 312 | |||
| 313 | -int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev); | ||
| 314 | +int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev); | ||
| 315 | +int rm_rf_child(int fd, const char *name, RemoveFlags flags); | ||
| 316 | int rm_rf(const char *path, RemoveFlags flags); | ||
| 317 | |||
| 318 | /* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ | ||
diff --git a/meta/recipes-core/systemd/systemd_244.5.bb b/meta/recipes-core/systemd/systemd_244.5.bb index b6f5a47d63..66446e2a7c 100644 --- a/meta/recipes-core/systemd/systemd_244.5.bb +++ b/meta/recipes-core/systemd/systemd_244.5.bb | |||
| @@ -28,6 +28,11 @@ SRC_URI += "file://touchscreen.rules \ | |||
| 28 | file://network-merge-link_drop-and-link_detach_from_manager.patch \ | 28 | file://network-merge-link_drop-and-link_detach_from_manager.patch \ |
| 29 | file://network-also-drop-requests-when-link-enters-linger-state.patch \ | 29 | file://network-also-drop-requests-when-link-enters-linger-state.patch \ |
| 30 | file://network-fix-Link-reference-counter-issue.patch \ | 30 | file://network-fix-Link-reference-counter-issue.patch \ |
| 31 | file://rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch \ | ||
| 32 | file://rm-rf-optionally-fsync-after-removing-directory-tree.patch \ | ||
| 33 | file://CVE-2021-3997-1.patch \ | ||
| 34 | file://CVE-2021-3997-2.patch \ | ||
| 35 | file://CVE-2021-3997-3.patch \ | ||
| 31 | " | 36 | " |
| 32 | 37 | ||
| 33 | # patches needed by musl | 38 | # patches needed by musl |
