Files
xen/tools/xenstore/xenstored_control.c
T
Pawel Wieczorkiewicz 36c80e29e3 tools/xenstored: Check number of strings passed to do_control()
It is possible to send a zero-string message body to xenstore's
XS_CONTROL handling function. Then the number of strings is used
for an array allocation. This leads to a crash in strcmp() in a
CONTROL sub-command invocation loop.
The output of xs_count_string() should be verified and all 0 or
negative values should be rejected with an EINVAL. At least the
sub-command name must be specified.

The xenstore crash can only be triggered from within dom0 (there
is a check in do_control() rejecting all non-dom0 requests with
an EACCES).

Testing: reproduced with the following command:
python -c 'print 16*"\x00"' | nc -U $XENSTORED_RUNDIR/socket

Signed-off-by: Pawel Wieczorkiewicz <wipawel@amazon.de>
Reviewed-by: Martin Pohlack <mpohlack@amazon.de>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
Reviewed-by: Juergen Gross <jgross@suse.com>
Acked-by: Wei Liu <wei.liu2@citrix.com>
Release-acked-by: Julien Grall <julien.grall@linaro.org>
2017-11-14 13:58:16 +00:00

201 lines
4.1 KiB
C

/*
Interactive commands for Xen Store Daemon.
Copyright (C) 2017 Juergen Gross, SUSE Linux GmbH
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "utils.h"
#include "talloc.h"
#include "xenstored_core.h"
#include "xenstored_control.h"
struct cmd_s {
char *cmd;
int (*func)(void *, struct connection *, char **, int);
char *pars;
};
static int do_control_check(void *ctx, struct connection *conn,
char **vec, int num)
{
if (num)
return EINVAL;
check_store();
send_ack(conn, XS_CONTROL);
return 0;
}
static int do_control_log(void *ctx, struct connection *conn,
char **vec, int num)
{
if (num != 1)
return EINVAL;
if (!strcmp(vec[0], "on"))
reopen_log();
else if (!strcmp(vec[0], "off"))
close_log();
else
return EINVAL;
send_ack(conn, XS_CONTROL);
return 0;
}
static int do_control_logfile(void *ctx, struct connection *conn,
char **vec, int num)
{
if (num != 1)
return EINVAL;
close_log();
talloc_free(tracefile);
tracefile = talloc_strdup(NULL, vec[0]);
reopen_log();
send_ack(conn, XS_CONTROL);
return 0;
}
static int do_control_memreport(void *ctx, struct connection *conn,
char **vec, int num)
{
FILE *fp;
int fd;
if (num > 1)
return EINVAL;
if (num == 0) {
if (tracefd < 0) {
if (!tracefile)
return EBADF;
fp = fopen(tracefile, "a");
} else {
/*
* Use dup() in order to avoid closing the file later
* with fclose() which will release stream resources.
*/
fd = dup(tracefd);
if (fd < 0)
return EBADF;
fp = fdopen(fd, "a");
if (!fp)
close(fd);
}
} else
fp = fopen(vec[0], "a");
if (!fp)
return EBADF;
talloc_report_full(NULL, fp);
fclose(fp);
send_ack(conn, XS_CONTROL);
return 0;
}
static int do_control_print(void *ctx, struct connection *conn,
char **vec, int num)
{
if (num != 1)
return EINVAL;
xprintf("control: %s", vec[0]);
send_ack(conn, XS_CONTROL);
return 0;
}
static int do_control_help(void *, struct connection *, char **, int);
static struct cmd_s cmds[] = {
{ "check", do_control_check, "" },
{ "log", do_control_log, "on|off" },
{ "logfile", do_control_logfile, "<file>" },
{ "memreport", do_control_memreport, "[<file>]" },
{ "print", do_control_print, "<string>" },
{ "help", do_control_help, "" },
};
static int do_control_help(void *ctx, struct connection *conn,
char **vec, int num)
{
int cmd, len = 0;
char *resp;
if (num)
return EINVAL;
for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) {
len += strlen(cmds[cmd].cmd) + 1;
len += strlen(cmds[cmd].pars) + 1;
}
len++;
resp = talloc_array(ctx, char, len);
if (!resp)
return ENOMEM;
len = 0;
for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) {
strcpy(resp + len, cmds[cmd].cmd);
len += strlen(cmds[cmd].cmd);
resp[len] = '\t';
len++;
strcpy(resp + len, cmds[cmd].pars);
len += strlen(cmds[cmd].pars);
resp[len] = '\n';
len++;
}
resp[len] = 0;
send_reply(conn, XS_CONTROL, resp, len);
return 0;
}
int do_control(struct connection *conn, struct buffered_data *in)
{
int num;
int cmd;
char **vec;
if (conn->id != 0)
return EACCES;
num = xs_count_strings(in->buffer, in->used);
if (num < 1)
return EINVAL;
vec = talloc_array(in, char *, num);
if (!vec)
return ENOMEM;
if (get_strings(in, vec, num) != num)
return EIO;
for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++)
if (streq(vec[0], cmds[cmd].cmd))
return cmds[cmd].func(in, conn, vec + 1, num - 1);
return EINVAL;
}