Skip to content

Commit b1a71e7

Browse files
authored
Merge pull request #1753 from giuseppe/fix-opening-root
linux: treats empty path to safe_openat as root
2 parents 8622175 + 64a2e0e commit b1a71e7

File tree

4 files changed

+119
-41
lines changed

4 files changed

+119
-41
lines changed

src/libcrun/linux.c

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ cleanup_private_data (void *private_data)
168168
if (p->dev_fds)
169169
cleanup_close_mapp (&(p->dev_fds));
170170

171+
if (p->rootfsfd >= 0)
172+
close (p->rootfsfd);
173+
171174
free (p->unified_cgroup_path);
172175
free (p->host_notify_socket_path);
173176
free (p->container_notify_socket_path);
@@ -1028,7 +1031,6 @@ do_masked_or_readonly_path (libcrun_container_t *container, const char *rel_path
10281031
{
10291032
unsigned long mount_flags = 0;
10301033
const char *rootfs = get_private_data (container)->rootfs;
1031-
int rootfsfd = get_private_data (container)->rootfsfd;
10321034
cleanup_close int pathfd = -1;
10331035
struct statfs sfs;
10341036
int ret;
@@ -1037,7 +1039,7 @@ do_masked_or_readonly_path (libcrun_container_t *container, const char *rel_path
10371039
if (rel_path[0] == '/')
10381040
rel_path++;
10391041

1040-
pathfd = safe_openat (rootfsfd, rootfs, rel_path, O_PATH | O_CLOEXEC, 0, err);
1042+
pathfd = safe_openat (get_private_data (container)->rootfsfd, rootfs, rel_path, O_PATH | O_CLOEXEC, 0, err);
10411043
if (UNLIKELY (pathfd < 0))
10421044
{
10431045
if (errno != ENOENT && errno != EACCES)
@@ -1226,6 +1228,17 @@ do_mount (libcrun_container_t *container, const char *source, int targetfd,
12261228
if (UNLIKELY (fd < 0))
12271229
return fd;
12281230

1231+
/* We are replacing the rootfs, reopen it. */
1232+
if (is_empty_string (target))
1233+
{
1234+
int tmp = dup (fd);
1235+
if (UNLIKELY (tmp < 0))
1236+
return crun_make_error (err, errno, "dup");
1237+
1238+
TEMP_FAILURE_RETRY (close (get_private_data (container)->rootfsfd));
1239+
get_private_data (container)->rootfsfd = tmp;
1240+
}
1241+
12291242
#ifdef HAVE_FGETXATTR
12301243
if (label_how == LABEL_XATTR)
12311244
{
@@ -1586,7 +1599,6 @@ libcrun_create_dev (libcrun_container_t *container, int devfd, int srcfd,
15861599
mode_t type = (device->type[0] == 'b') ? S_IFBLK : ((device->type[0] == 'p') ? S_IFIFO : S_IFCHR);
15871600
const char *fullname = device->path;
15881601
cleanup_close int fd = -1;
1589-
int rootfsfd = get_private_data (container)->rootfsfd;
15901602
const char *rootfs = get_private_data (container)->rootfs;
15911603
if (is_empty_string (fullname))
15921604
return crun_make_error (err, EINVAL, "device path is empty");
@@ -1617,7 +1629,7 @@ libcrun_create_dev (libcrun_container_t *container, int devfd, int srcfd,
16171629
{
16181630
const char *rel_path = consume_slashes (normalized_path);
16191631

1620-
fd = crun_safe_create_and_open_ref_at (false, rootfsfd, rootfs, rel_path, 0755, err);
1632+
fd = crun_safe_create_and_open_ref_at (false, get_private_data (container)->rootfsfd, rootfs, rel_path, 0755, err);
16211633
if (UNLIKELY (fd < 0))
16221634
return fd;
16231635
}
@@ -1682,18 +1694,18 @@ libcrun_create_dev (libcrun_container_t *container, int devfd, int srcfd,
16821694

16831695
if (dirname[0] == '\0')
16841696
{
1685-
dirfd = dup (rootfsfd);
1697+
dirfd = dup (get_private_data (container)->rootfsfd);
16861698
if (UNLIKELY (dirfd < 0))
16871699
return crun_make_error (err, errno, "dup fd for `%s`", rootfs);
16881700
}
16891701
else
16901702
{
1691-
dirfd = safe_openat (rootfsfd, rootfs, dirname, O_DIRECTORY | O_PATH | O_CLOEXEC, 0, err);
1703+
dirfd = safe_openat (get_private_data (container)->rootfsfd, rootfs, dirname, O_DIRECTORY | O_PATH | O_CLOEXEC, 0, err);
16921704
if (dirfd < 0 && ensure_parent_dir)
16931705
{
16941706
crun_error_release (err);
16951707

1696-
dirfd = crun_safe_create_and_open_ref_at (true, rootfsfd, rootfs, dirname, 0755, err);
1708+
dirfd = crun_safe_create_and_open_ref_at (true, get_private_data (container)->rootfsfd, rootfs, dirname, 0755, err);
16971709
}
16981710
if (UNLIKELY (dirfd < 0))
16991711
return dirfd;
@@ -1749,13 +1761,12 @@ create_missing_devs (libcrun_container_t *container, bool binds, libcrun_error_t
17491761
cleanup_close int devfd = -1;
17501762
runtime_spec_schema_config_schema *def = container->container_def;
17511763
const char *rootfs = get_private_data (container)->rootfs;
1752-
int rootfsfd = get_private_data (container)->rootfsfd;
17531764
cleanup_close_map struct libcrun_fd_map *dev_fds = NULL;
17541765

17551766
dev_fds = get_private_data (container)->dev_fds;
17561767
get_private_data (container)->dev_fds = NULL;
17571768

1758-
devfd = openat (rootfsfd, "dev", O_CLOEXEC | O_PATH | O_DIRECTORY);
1769+
devfd = openat (get_private_data (container)->rootfsfd, "dev", O_CLOEXEC | O_PATH | O_DIRECTORY);
17591770
if (UNLIKELY (devfd < 0))
17601771
return crun_make_error (err, errno, "open `/dev` directory in `%s`", rootfs);
17611772

@@ -1910,7 +1921,6 @@ static int
19101921
append_tmpfs_mode_if_missing (libcrun_container_t *container, runtime_spec_schema_defs_mount *mount, char **data, libcrun_error_t *err)
19111922
{
19121923
const char *rootfs = get_private_data (container)->rootfs;
1913-
int rootfsfd = get_private_data (container)->rootfsfd;
19141924
bool empty_data = is_empty_string (*data);
19151925
cleanup_close int fd = -1;
19161926
struct stat st;
@@ -1919,7 +1929,7 @@ append_tmpfs_mode_if_missing (libcrun_container_t *container, runtime_spec_schem
19191929
if (*data != NULL && strstr (*data, "mode="))
19201930
return 0;
19211931

1922-
fd = safe_openat (rootfsfd, rootfs, mount->destination, O_CLOEXEC | O_RDONLY, 0, err);
1932+
fd = safe_openat (get_private_data (container)->rootfsfd, rootfs, mount->destination, O_CLOEXEC | O_RDONLY, 0, err);
19231933
if (fd < 0)
19241934
{
19251935
if (crun_error_get_errno (err) != ENOENT)
@@ -2046,13 +2056,13 @@ get_force_cgroup_v1_annotation (libcrun_container_t *container)
20462056
}
20472057

20482058
static int
2049-
do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, libcrun_error_t *err)
2059+
do_mounts (libcrun_container_t *container, const char *rootfs, libcrun_error_t *err)
20502060
{
2051-
size_t i;
2052-
int ret;
20532061
runtime_spec_schema_config_schema *def = container->container_def;
20542062
const char *systemd_cgroup_v1 = get_force_cgroup_v1_annotation (container);
20552063
cleanup_close_map struct libcrun_fd_map *mount_fds = NULL;
2064+
size_t i;
2065+
int ret;
20562066

20572067
mount_fds = get_private_data (container)->mount_fds;
20582068
get_private_data (container)->mount_fds = NULL;
@@ -2134,7 +2144,7 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, lib
21342144
if (UNLIKELY (len < 0))
21352145
return len;
21362146

2137-
ret = safe_create_symlink (rootfsfd, rootfs, target, def->mounts[i]->destination, err);
2147+
ret = safe_create_symlink (get_private_data (container)->rootfsfd, rootfs, target, def->mounts[i]->destination, err);
21382148
if (UNLIKELY (ret < 0))
21392149
return ret;
21402150

@@ -2143,20 +2153,20 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, lib
21432153
else if (is_sysfs_or_proc)
21442154
{
21452155
/* Enforce sysfs and proc to be mounted on a regular directory. */
2146-
ret = openat (rootfsfd, target, O_CLOEXEC | O_NOFOLLOW | O_DIRECTORY);
2156+
ret = openat (get_private_data (container)->rootfsfd, target, O_CLOEXEC | O_NOFOLLOW | O_DIRECTORY);
21472157
if (UNLIKELY (ret < 0))
21482158
{
21492159
if (errno == ENOENT)
21502160
{
21512161
if (strchr (target, '/'))
21522162
return crun_make_error (err, 0, "invalid target `%s`: it must be mounted at the root", target);
21532163

2154-
ret = mkdirat (rootfsfd, target, 0755);
2164+
ret = mkdirat (get_private_data (container)->rootfsfd, target, 0755);
21552165
if (UNLIKELY (ret < 0))
21562166
return crun_make_error (err, errno, "mkdirat `%s`", target);
21572167

21582168
/* Try opening it again. */
2159-
ret = openat (rootfsfd, target, O_CLOEXEC | O_NOFOLLOW | O_DIRECTORY);
2169+
ret = openat (get_private_data (container)->rootfsfd, target, O_CLOEXEC | O_NOFOLLOW | O_DIRECTORY);
21602170
}
21612171
else if (errno == ENOTDIR)
21622172
return crun_make_error (err, errno, "the target `/%s` is invalid", target);
@@ -2172,7 +2182,7 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, lib
21722182
bool is_dir = S_ISDIR (src_mode);
21732183

21742184
/* Make sure any other directory/file is created and take a O_PATH reference to it. */
2175-
ret = crun_safe_create_and_open_ref_at (is_dir, rootfsfd, rootfs, target, is_dir ? 01755 : 0755, err);
2185+
ret = crun_safe_create_and_open_ref_at (is_dir, get_private_data (container)->rootfsfd, rootfs, target, is_dir ? 01755 : 0755, err);
21762186
if (UNLIKELY (ret < 0))
21772187
return ret;
21782188

@@ -2245,7 +2255,7 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, lib
22452255
{
22462256
int destfd, tmpfd;
22472257

2248-
destfd = safe_openat (rootfsfd, rootfs, target, O_CLOEXEC | O_DIRECTORY, 0, err);
2258+
destfd = safe_openat (get_private_data (container)->rootfsfd, rootfs, target, O_CLOEXEC | O_DIRECTORY, 0, err);
22492259
if (UNLIKELY (destfd < 0))
22502260
return crun_error_wrap (err, "open `%s` to write for tmpcopyup", target);
22512261

@@ -2262,7 +2272,7 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, lib
22622272
const bool is_dir = S_ISDIR (src_mode);
22632273
cleanup_close int dfd = -1;
22642274

2265-
dfd = safe_openat (rootfsfd, rootfs, target, O_RDONLY | O_PATH | O_CLOEXEC | (is_dir ? O_DIRECTORY : 0), 0, err);
2275+
dfd = safe_openat (get_private_data (container)->rootfsfd, rootfs, target, O_RDONLY | O_PATH | O_CLOEXEC | (is_dir ? O_DIRECTORY : 0), 0, err);
22662276
if (UNLIKELY (dfd < 0))
22672277
return crun_make_error (err, errno, "open mount target `/%s`", target);
22682278

@@ -2283,7 +2293,6 @@ do_mounts (libcrun_container_t *container, int rootfsfd, const char *rootfs, lib
22832293
int
22842294
libcrun_container_do_bind_mount (libcrun_container_t *container, char *mount_source, char *mount_destination, char **mount_options, size_t mount_options_len, libcrun_error_t *err)
22852295
{
2286-
int ret, rootfsfd;
22872296
const char *target = consume_slashes (mount_destination);
22882297
cleanup_free char *data = NULL;
22892298
unsigned long flags = 0;
@@ -2293,9 +2302,9 @@ libcrun_container_do_bind_mount (libcrun_container_t *container, char *mount_sou
22932302
uint64_t rec_clear = 0;
22942303
uint64_t rec_set = 0;
22952304
const char *rootfs = get_private_data (container)->rootfs;
2296-
rootfsfd = get_private_data (container)->rootfsfd;
2305+
int ret;
22972306

2298-
if ((rootfsfd < 0) || (rootfs == NULL))
2307+
if ((get_private_data (container)->rootfsfd < 0) || (rootfs == NULL))
22992308
return crun_make_error (err, 0, "invalid rootfs state while performing bind mount from external plugin or handler");
23002309

23012310
if (mount_options == NULL)
@@ -2321,7 +2330,7 @@ libcrun_container_do_bind_mount (libcrun_container_t *container, char *mount_sou
23212330
}
23222331

23232332
/* Make sure any other directory/file is created and take a O_PATH reference to it. */
2324-
ret = crun_safe_create_and_open_ref_at (is_dir, rootfsfd, rootfs, target, is_dir ? 01755 : 0755, err);
2333+
ret = crun_safe_create_and_open_ref_at (is_dir, get_private_data (container)->rootfsfd, rootfs, target, is_dir ? 01755 : 0755, err);
23252334
if (UNLIKELY (ret < 0))
23262335
return ret;
23272336

@@ -2577,9 +2586,7 @@ int
25772586
libcrun_set_mounts (struct container_entrypoint_s *entrypoint_args, libcrun_container_t *container, const char *rootfs, set_mounts_cb_t cb, void *cb_data, libcrun_error_t *err)
25782587
{
25792588
runtime_spec_schema_config_schema *def = container->container_def;
2580-
cleanup_close int rootfsfd_cleanup = -1;
25812589
unsigned long rootfs_propagation = 0;
2582-
int rootfsfd = -1;
25832590
int cgroup_mode;
25842591
int is_user_ns = 0;
25852592
int ret = 0;
@@ -2610,12 +2617,12 @@ libcrun_set_mounts (struct container_entrypoint_s *entrypoint_args, libcrun_cont
26102617
return ret;
26112618
}
26122619

2613-
rootfsfd = rootfsfd_cleanup = open (rootfs, O_PATH | O_CLOEXEC);
2614-
if (UNLIKELY (rootfsfd < 0))
2620+
ret = open (rootfs, O_PATH | O_CLOEXEC);
2621+
if (UNLIKELY (ret < 0))
26152622
return crun_make_error (err, errno, "open `%s`", rootfs);
26162623

2624+
get_private_data (container)->rootfsfd = ret;
26172625
get_private_data (container)->rootfs = rootfs;
2618-
get_private_data (container)->rootfsfd = rootfsfd;
26192626

26202627
// configure handler mounts
26212628
ret = libcrun_container_notify_handler (entrypoint_args, HANDLER_CONFIGURE_MOUNTS, container, rootfs, err);
@@ -2628,7 +2635,7 @@ libcrun_set_mounts (struct container_entrypoint_s *entrypoint_args, libcrun_cont
26282635
unsigned long remount_flags = MS_REMOUNT | MS_BIND | MS_RDONLY;
26292636
int fd;
26302637

2631-
fd = dup (rootfsfd);
2638+
fd = dup (get_private_data (container)->rootfsfd);
26322639
if (UNLIKELY (fd < 0))
26332640
return crun_make_error (err, errno, "dup fd for `%s`", rootfs);
26342641

@@ -2656,7 +2663,7 @@ libcrun_set_mounts (struct container_entrypoint_s *entrypoint_args, libcrun_cont
26562663
if (UNLIKELY (ret < 0))
26572664
return ret;
26582665

2659-
ret = do_mounts (container, rootfsfd, rootfs, err);
2666+
ret = do_mounts (container, rootfs, err);
26602667
if (UNLIKELY (ret < 0))
26612668
return ret;
26622669

@@ -2692,7 +2699,7 @@ libcrun_set_mounts (struct container_entrypoint_s *entrypoint_args, libcrun_cont
26922699
libcrun_error_t tmp_err = NULL;
26932700
const char *rel_cwd = consume_slashes (def->process->cwd);
26942701
/* Ignore errors here and let it fail later. */
2695-
(void) crun_safe_ensure_directory_at (rootfsfd, rootfs, rel_cwd, 0755, &tmp_err);
2702+
(void) crun_safe_ensure_directory_at (get_private_data (container)->rootfsfd, rootfs, rel_cwd, 0755, &tmp_err);
26962703
crun_error_release (&tmp_err);
26972704
}
26982705

@@ -2709,7 +2716,7 @@ libcrun_set_mounts (struct container_entrypoint_s *entrypoint_args, libcrun_cont
27092716
if (UNLIKELY (ret < 0))
27102717
return crun_make_error (err, errno, "failed configuring mounts for handler at phase: HANDLER_CONFIGURE_AFTER_MOUNTS");
27112718

2712-
get_private_data (container)->rootfsfd = -1;
2719+
close_and_reset (&(get_private_data (container)->rootfsfd));
27132720

27142721
return 0;
27152722
}
@@ -4304,7 +4311,7 @@ prepare_and_send_dev_mounts (libcrun_container_t *container, int sync_socket_hos
43044311
return ret;
43054312

43064313
ret = mkdir (devs_path, 0700);
4307-
if (UNLIKELY (ret < 0) && errno != EEXIST)
4314+
if (UNLIKELY (ret < 0 && errno != EEXIST))
43084315
return crun_make_error (err, errno, "mkdir `%s`", devs_path);
43094316

43104317
current_mountns = open ("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);

src/libcrun/utils.c

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,25 @@ crun_ensure_directory_at (int dirfd, const char *path, int mode, bool nofollow,
298298
return 0;
299299
}
300300

301+
static int
302+
check_fd_is_path (const char *path, int fd, const char *fdname, libcrun_error_t *err)
303+
{
304+
proc_fd_path_t fdpath;
305+
size_t path_len = strlen (path);
306+
char link[PATH_MAX];
307+
int ret;
308+
309+
get_proc_self_fd_path (fdpath, fd);
310+
ret = TEMP_FAILURE_RETRY (readlink (fdpath, link, sizeof (link)));
311+
if (UNLIKELY (ret < 0))
312+
return crun_make_error (err, errno, "readlink `%s`", fdname);
313+
314+
if (((size_t) ret) != path_len || memcmp (link, path, path_len))
315+
return crun_make_error (err, 0, "target `%s` does not point to the directory `%s`", fdname, path);
316+
317+
return 0;
318+
}
319+
301320
static int
302321
check_fd_under_path (const char *rootfs, size_t rootfslen, int fd, const char *fdname, libcrun_error_t *err)
303322
{
@@ -377,6 +396,23 @@ safe_openat (int dirfd, const char *rootfs, const char *path, int flags, int mod
377396
static bool openat2_supported = true;
378397
int ret;
379398

399+
if (is_empty_string (path))
400+
{
401+
cleanup_close int fd = -1;
402+
403+
fd = open (rootfs, flags, mode);
404+
if (UNLIKELY (fd < 0))
405+
return crun_make_error (err, errno, "open `%s`", rootfs);
406+
407+
ret = check_fd_is_path (rootfs, fd, path, err);
408+
if (UNLIKELY (ret < 0))
409+
return ret;
410+
411+
ret = fd;
412+
fd = -1;
413+
return ret;
414+
}
415+
380416
if (openat2_supported)
381417
{
382418
repeat:
@@ -449,7 +485,11 @@ crun_safe_ensure_at (bool do_open, bool dir, int dirfd, const char *dirpath,
449485

450486
/* Empty path, nothing to do. */
451487
if (*path == '\0')
452-
return 0;
488+
{
489+
if (do_open)
490+
return open (dirpath, O_CLOEXEC | O_PATH, 0);
491+
return 0;
492+
}
453493

454494
npath = xstrdup (path);
455495

@@ -577,12 +617,12 @@ crun_safe_ensure_at (bool do_open, bool dir, int dirfd, const char *dirpath,
577617
int
578618
crun_safe_create_and_open_ref_at (bool dir, int dirfd, const char *dirpath, const char *path, int mode, libcrun_error_t *err)
579619
{
580-
int fd;
620+
int ret;
581621

582622
/* If the file/dir already exists, just open it. */
583-
fd = safe_openat (dirfd, dirpath, path, O_PATH | O_CLOEXEC, 0, err);
584-
if (LIKELY (fd >= 0))
585-
return fd;
623+
ret = safe_openat (dirfd, dirpath, path, O_PATH | O_CLOEXEC, 0, err);
624+
if (LIKELY (ret >= 0))
625+
return ret;
586626

587627
crun_error_release (err);
588628
return crun_safe_ensure_at (true, dir, dirfd, dirpath, path, mode, MAX_READLINKS, err);

0 commit comments

Comments
 (0)