criu/seccomp.c
Andrew Vagin d55e13d4f3 seccomp: don't leak info on an error path
CID 154852 (#1 of 3): Resource leak (RESOURCE_LEAK)
32. leaked_storage: Variable info going out of scope leaks the storage it points to.

Reported-by: coverity
Signed-off-by: Andrew Vagin <avagin@virtuozzo.com>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2015-11-30 11:39:43 +03:00

272 lines
5.6 KiB
C

#include <linux/filter.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "config.h"
#include "imgset.h"
#include "kcmp.h"
#include "pstree.h"
#include "ptrace.h"
#include "proc_parse.h"
#include "seccomp.h"
#include "servicefd.h"
#include "util.h"
#include "rst-malloc.h"
#include "protobuf.h"
#include "protobuf/seccomp.pb-c.h"
/* populated on dump during collect_seccomp_filters() */
static int next_filter_id = 0;
static struct seccomp_info **filters = NULL;
static struct seccomp_info *find_inherited(struct pstree_item *parent,
struct sock_filter *filter, int len)
{
struct seccomp_info *info;
/* if we have no filters yet, this one has no parent */
if (!filters)
return NULL;
for (info = filters[dmpi(parent)->pi_creds->last_filter]; info; info = info->prev) {
if (len != info->filter.filter.len)
continue;
if (!memcmp(filter, info->filter.filter.data, len))
return info;
}
return NULL;
}
static int collect_filter_for_pstree(struct pstree_item *item)
{
struct seccomp_info *infos = NULL, *cursor;
int info_count, i, ret = -1;
struct sock_filter buf[BPF_MAXINSNS];
void *m;
if (item->state == TASK_DEAD ||
dmpi(item)->pi_creds->seccomp_mode != SECCOMP_MODE_FILTER)
return 0;
for (i = 0; true; i++) {
int len;
struct seccomp_info *info, *inherited = NULL;
len = ptrace(PTRACE_SECCOMP_GET_FILTER, item->pid.real, i, buf);
if (len < 0) {
if (errno == ENOENT) {
/* end of the search */
BUG_ON(i == 0);
goto save_infos;
} else if (errno == EINVAL) {
pr_err("dumping seccomp infos not supported\n");
goto out;
} else {
pr_perror("couldn't dump seccomp filter");
goto out;
}
}
inherited = find_inherited(item->parent, buf, len);
if (inherited) {
bool found = false;
/* Small sanity check: if infos is already populated,
* we should have inherited that filter too. */
for (cursor = infos; cursor; cursor = cursor->prev) {
if (inherited->prev== cursor) {
found = true;
break;
}
}
BUG_ON(!found);
infos = inherited;
continue;
}
info = xmalloc(sizeof(*info));
if (!info)
goto out;
seccomp_filter__init(&info->filter);
info->filter.filter.len = len * sizeof(struct sock_filter);
info->filter.filter.data = xmalloc(info->filter.filter.len);
if (!info->filter.filter.data) {
xfree(info);
goto out;
}
memcpy(info->filter.filter.data, buf, info->filter.filter.len);
info->prev = infos;
infos = info;
}
save_infos:
info_count = i;
m = xrealloc(filters, sizeof(*filters) * (next_filter_id + info_count));
if (!m)
goto out;
filters = m;
for (cursor = infos, i = info_count + next_filter_id - 1;
i >= next_filter_id; i--) {
BUG_ON(!cursor);
cursor->id = i;
filters[i] = cursor;
cursor = cursor->prev;
}
next_filter_id += info_count;
dmpi(item)->pi_creds->last_filter = infos->id;
/* Don't free the part of the tree we just successfully acquired */
infos = NULL;
ret = 0;
out:
while (infos) {
struct seccomp_info *freeme = infos;
infos = infos->prev;
xfree(freeme->filter.filter.data);
xfree(freeme);
}
return ret;
}
static int dump_seccomp_filters(void)
{
SeccompEntry se = SECCOMP_ENTRY__INIT;
int ret = -1, i;
/* If we didn't collect any filters, don't create a seccomp image at all. */
if (next_filter_id == 0)
return 0;
se.seccomp_filters = xzalloc(sizeof(*se.seccomp_filters) * next_filter_id);
if (!se.seccomp_filters)
return -1;
se.n_seccomp_filters = next_filter_id;
for (i = 0; i < next_filter_id; i++) {
SeccompFilter *sf;
struct seccomp_info *cur = filters[i];
sf = se.seccomp_filters[cur->id] = &cur->filter;
if (cur->prev) {
sf->has_prev = true;
sf->prev = cur->prev->id;
}
}
ret = pb_write_one(img_from_set(glob_imgset, CR_FD_SECCOMP), &se, PB_SECCOMP);
xfree(se.seccomp_filters);
for (i = 0; i < next_filter_id; i++) {
struct seccomp_info *freeme = filters[i];
xfree(freeme->filter.filter.data);
xfree(freeme);
}
xfree(filters);
return ret;
}
int collect_seccomp_filters(void)
{
if (preorder_pstree_traversal(root_item, collect_filter_for_pstree) < 0)
return -1;
if (dump_seccomp_filters())
return -1;
return 0;
}
/* Populated on restore by prepare_seccomp_filters */
static SeccompEntry *se;
int prepare_seccomp_filters(void)
{
struct cr_img *img;
int ret;
img = open_image(CR_FD_SECCOMP, O_RSTR);
if (!img)
return -1;
ret = pb_read_one_eof(img, &se, PB_SECCOMP);
close_image(img);
if (ret <= 0)
return 0; /* there were no filters */
BUG_ON(!se);
return 0;
}
int seccomp_filters_get_rst_pos(CoreEntry *core, int *count, unsigned long *pos)
{
SeccompFilter *sf = NULL;
struct sock_fprog *arr = NULL;
void *filter_data = NULL;
int ret = -1, i;
size_t filter_size = 0;
if (!core->tc->has_seccomp_filter) {
*count = 0;
return 0;
}
*count = 0;
*pos = rst_mem_cpos(RM_PRIVATE);
BUG_ON(core->tc->seccomp_filter > se->n_seccomp_filters);
sf = se->seccomp_filters[core->tc->seccomp_filter];
while (1) {
(*count)++;
filter_size += sf->filter.len;
if (!sf->has_prev)
break;
sf = se->seccomp_filters[sf->prev];
}
arr = rst_mem_alloc(sizeof(struct sock_fprog) * (*count) + filter_size, RM_PRIVATE);
if (!arr)
goto out;
filter_data = &arr[*count];
sf = se->seccomp_filters[core->tc->seccomp_filter];
for (i = 0; i < *count; i++) {
struct sock_fprog *fprog = &arr[i];
BUG_ON(sf->filter.len % sizeof(struct sock_filter));
fprog->len = sf->filter.len / sizeof(struct sock_filter);
memcpy(filter_data, sf->filter.data, sf->filter.len);
filter_data += sf->filter.len;
sf = se->seccomp_filters[sf->prev];
}
ret = 0;
out:
seccomp_entry__free_unpacked(se, NULL);
return ret;
}