export PATH="$(pwd)/cov-analysis-linux64/bin:${PATH}"
meson setup -Ddocs=false -Dtests=true -Dinit-script=systemd -Dprefix=/usr build/
- meson compile -C build
- cd build
# Build
- cov-build --dir cov-int make -j4
+ cov-build --dir cov-int ninja -C build
tar czvf lxcfs.tgz cov-int
# Submit the results
env:
CC: ${{ matrix.compiler }}
run: |
- meson setup -Ddocs=false -Dtests=true -Dinit-script=systemd -Dprefix=/usr build/
+ meson setup -Ddocs=false -Dtests=true -Dinit-script=systemd -Dprefix=/usr -Db_sanitize=address,undefined build/
meson compile -C build
- name: Test
env:
env:
CC: ${{ matrix.compiler }}
run: |
- meson setup -Ddocs=false -Dtests=true -Dinit-script=systemd -Dprefix=/usr build/
+ meson setup -Ddocs=false -Dtests=true -Dinit-script=systemd -Dprefix=/usr -Db_sanitize=address,undefined build/
meson compile -C build
- name: Test
env:
meson compile -C build/
sudo meson install -C build/
+To build with sanitizers you have to specify `-Db_sanitize=...` option to `meson setup`.
+For example, to enable ASAN and UBSAN:
+
+ meson setup -Dinit-script=systemd --prefix=/usr build/ -Db_sanitize=address,undefined
+ meson compile -C build/
+
## Usage
The recommended command to run lxcfs is:
-v /var/lib/lxcfs/proc/swaps:/proc/swaps:rw \
-v /var/lib/lxcfs/proc/uptime:/proc/uptime:rw \
-v /var/lib/lxcfs/proc/slabinfo:/proc/slabinfo:rw \
+ -v /var/lib/lxcfs/sys/devices/system/cpu:/sys/devices/system/cpu:rw \
ubuntu:18.04 /bin/bash
```
if 'systemd' in init_script
systemd = dependency('systemd')
- systemd_system_unit_dir = systemd.get_pkgconfig_variable('systemdsystemunitdir')
+ systemd_system_unit_dir = get_option('prefix') + systemd.get_pkgconfig_variable('systemdsystemunitdir')
systemd_service = custom_target(
'lxcfs.service',
input: 'systemd/lxcfs.service.in',
static bool can_use_sys_cpu;
static bool has_versioned_opts;
static bool memory_is_cgroupv2;
+static __u32 host_personality;
static volatile sig_atomic_t reload_successful;
return memory_is_cgroupv2;
}
+__u32 liblxcfs_personality(void)
+{
+ return host_personality;
+}
+
/* Define pivot_root() if missing from the C library */
#ifndef HAVE_PIVOT_ROOT
static int pivot_root(const char *new_root, const char *put_old)
}
#elif defined(__ia64__)
/* On ia64 the stack and stack size are passed as separate arguments. */
- return syscall(__NR_clone, flags | SIGCHLD, NULL, prctl_arg(0), pidfd);
+ return syscall(__NR_clone, flags | SIGCHLD, NULL, 0, pidfd);
#else
return syscall(__NR_clone, flags | SIGCHLD, NULL, pidfd);
#endif
goto broken_upgrade;
}
+ if (get_task_personality(getpid(), &host_personality) < 0) {
+ lxcfs_info("Failed to retrieve host personality");
+ goto broken_upgrade;
+ }
+
reload_successful = 1;
return;
extern bool liblxcfs_memory_is_cgroupv2(void);
extern bool liblxcfs_can_use_sys_cpu(void);
extern bool liblxcfs_has_versioned_opts(void);
+extern __u32 liblxcfs_personality(void);
static inline bool lxcfs_has_opt(struct lxcfs_opts *opts, lxcfs_opt_t opt)
{
{
char *r;
+ if (!strlen(c))
+ return NULL;
+
r = strchr(c + 1, ',');
return r ? (r + 1) : NULL;
}
if (lxcfs_init_library() < 0)
return NULL;
+#if HAVE_FUSE3
+ cfg->direct_io = 1;
+#endif
+
return fuse_get_context()->private_data;
}
}
if (append_comma_separate(&new_fuse_opts, "direct_io")) {
- lxcfs_error("Failed to copy fuse argument \"nonempty\"");
+ lxcfs_error("Failed to copy fuse argument \"direct_io\"");
goto out;
}
#endif
? 20 \
: sizeof(int[-2 * (sizeof(type) > 8)])))
+#define strnprintf(buf, buf_size, ...) \
+ ({ \
+ int __ret_strnprintf; \
+ __ret_strnprintf = snprintf(buf, buf_size, ##__VA_ARGS__); \
+ if (__ret_strnprintf < 0 || (size_t)__ret_strnprintf >= (size_t)buf_size) \
+ __ret_strnprintf = ret_errno(EIO); \
+ __ret_strnprintf; \
+ })
+
#define move_ptr(ptr) \
({ \
__typeof__(ptr) __internal_ptr__ = (ptr); \
}
out_rwlock_unlock:
+ pthread_mutex_lock(&rv->lock);
pthread_rwlock_unlock(&head->lock);
return move_ptr(rv);
}
return faccessat(cfd, path, F_OK, 0) == 0;
}
+/* should be called with wr-locked list */
static struct cg_proc_stat *prune_proc_stat_list(struct cg_proc_stat *node)
{
struct cg_proc_stat *first = NULL;
if (!cgroup_supports("cpu", node->cg, "cpu.shares")) {
struct cg_proc_stat *cur = node;
+ /*
+ * We need to ensure that no one referenced this node,
+ * because we are going to remove it from the list and free memory.
+ *
+ * If we can't grab the lock then just keep this node for now.
+ */
+ if (pthread_mutex_trylock(&cur->lock))
+ goto next;
+
+ /*
+ * Yes, we can put lock back just after taking it, as we ensured
+ * that we are only one user of it right now.
+ *
+ * It follows from three facts:
+ * - we are under pthread_rwlock_wrlock(hash_table_bucket)
+ * - pthread_mutex_lock is taken by find_proc_stat_node()
+ * with pthread_rwlock_rdlock(hash_table_bucket) held.
+ * - pthread_mutex_lock is taken by add_proc_stat_node()
+ * with pthread_rwlock_wrlock(hash_table_bucket) held.
+ *
+ * It means that nobody can get a pointer to (cur) node in a parallel
+ * thread and all old users of (cur) node have released pthread_mutex_lock(cur).
+ */
+ pthread_mutex_unlock(&cur->lock);
+
if (prev)
prev->next = node->next;
else
free_proc_stat_node(cur);
} else {
+next:
if (!first)
first = node;
prev = node;
node = head->next;
do {
- if (strcmp(cg, node->cg) == 0)
+ if (strcmp(cg, node->cg) == 0) {
+ pthread_mutex_lock(&node->lock);
goto out;
+ }
} while ((node = node->next));
node = NULL;
lxcfs_debug("New stat node (%d) for %s\n", cpu_count, cg);
}
- pthread_mutex_lock(&node->lock);
-
/*
* If additional CPUs on the host have been enabled, CPU usage counter
* arrays have to be expanded.
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/param.h>
+#include <sys/personality.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
return total_len;
}
+static int proc_read_with_personality(int (*do_proc_read)(char *, size_t, off_t,
+ struct fuse_file_info *), char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ struct fuse_context *fc = fuse_get_context();
+ __u32 host_personality = liblxcfs_personality(), caller_personality;
+ bool change_personality;
+ int ret, read_ret;
+
+ if (get_task_personality(fc->pid, &caller_personality) < 0)
+ return log_error(0, "Failed to get caller process (pid: %d) personality", fc->pid);
+
+ /* do we need to change thread personality? */
+ change_personality = host_personality != caller_personality;
+
+ if (change_personality) {
+ ret = personality(caller_personality);
+ if (ret == -1)
+ return log_error(0, "Call to personality(%d) failed: %s\n",
+ caller_personality, strerror(errno));
+
+ lxcfs_debug("task (tid: %d) personality was changed %d -> %d\n",
+ (int)syscall(SYS_gettid), ret, caller_personality);
+ }
+
+ read_ret = do_proc_read(buf, size, offset, fi);
+
+ if (change_personality) {
+ ret = personality(host_personality);
+ if (ret == -1)
+ return log_error(0, "Call to personality(%d) failed: %s\n",
+ host_personality, strerror(errno));
+
+ lxcfs_debug("task (tid: %d) personality was restored %d -> %d\n",
+ (int)syscall(SYS_gettid), ret, host_personality);
+ }
+
+ return read_ret;
+}
+
__lxcfs_fuse_ops int proc_read(const char *path, char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
buf, size, offset, f);
case LXC_TYPE_PROC_CPUINFO:
if (liblxcfs_functional())
- return proc_cpuinfo_read(buf, size, offset, fi);
+ return proc_read_with_personality(&proc_cpuinfo_read, buf, size, offset, fi);
return read_file_fuse_with_offset(LXC_TYPE_PROC_CPUINFO_PATH,
buf, size, offset, f);
return 0;
}
+int safe_uint32(const char *numstr, uint32_t *converted, int base)
+{
+ char *err = NULL;
+ unsigned long uli;
+
+ while (isspace(*numstr))
+ numstr++;
+
+ if (*numstr == '-')
+ return -EINVAL;
+
+ errno = 0;
+ uli = strtoul(numstr, &err, base);
+ if (errno == ERANGE && uli == UINT32_MAX)
+ return -ERANGE;
+
+ if (err == numstr || *err != '\0')
+ return -EINVAL;
+
+ *converted = (uint32_t)uli;
+ return 0;
+}
+
static int char_left_gc(const char *buffer, size_t len)
{
size_t i;
return dirp;
}
+
+int get_task_personality(pid_t pid, __u32 *personality)
+{
+ __do_close int fd = -EBADF;
+ int ret = -1;
+ char path[STRLITERALLEN("/proc//personality") + INTTYPE_TO_STRLEN(pid_t) + 1];
+ /* seq_printf(m, "%08x\n", task->personality); */
+ char buf[8 + 1];
+
+ ret = strnprintf(path, sizeof(path), "/proc/%d/personality", pid);
+ if (ret < 0)
+ return -1;
+
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
+ return -1;
+
+ ret = read_nointr(fd, buf, sizeof(buf) - 1);
+ if (ret >= 0) {
+ buf[ret] = '\0';
+ if (safe_uint32(buf, personality, 16) < 0)
+ return log_error(-1, "Failed to convert personality %s", buf);
+ }
+
+ return ret;
+}
extern DIR *opendir_flags(const char *path, int oflags);
extern ssize_t write_nointr(int fd, const void *buf, size_t count);
extern int safe_uint64(const char *numstr, uint64_t *converted, int base);
+extern int safe_uint32(const char *numstr, uint32_t *converted, int base);
extern char *trim_whitespace_in_place(char *buffer);
static inline bool file_exists(const char *f)
#define PROTECT_OPEN (PROTECT_OPEN_WITH_TRAILING_SYMLINKS | O_NOFOLLOW)
extern char *read_file_at(int dfd, const char *fnam, unsigned int o_flags);
+extern int get_task_personality(pid_t pid, __u32 *personality);
+extern int get_host_personality(__u32 *personality);
+
#endif /* __LXCFS_UTILS_H */