Skip to content

Commit 6329df5

Browse files
committed
Merge remote-tracking branch 'remotes/mdroth/tags/qga-pull-2020-04-15-tag' into staging
qemu-ga patch queue for hard-freeze * enforce 48MB limit for guest-file-read to avoid memory allocation failures # gpg: Signature made Wed 15 Apr 2020 15:23:48 BST # gpg: using RSA key CEACC9E15534EBABB82D3FA03353C9CEF108B584 # gpg: issuer "[email protected]" # gpg: Good signature from "Michael Roth <[email protected]>" [full] # gpg: aka "Michael Roth <[email protected]>" [full] # gpg: aka "Michael Roth <[email protected]>" [full] # Primary key fingerprint: CEAC C9E1 5534 EBAB B82D 3FA0 3353 C9CE F108 B584 * remotes/mdroth/tags/qga-pull-2020-04-15-tag: qga: Restrict guest-file-read count to 48 MB to avoid crashes qga: Extract qmp_guest_file_read() to common commands.c qga: Extract guest_file_handle_find() to commands-common.h Revert "prevent crash when executing guest-file-read with large count" Signed-off-by: Peter Maydell <[email protected]>
2 parents 73995d1 + 1329651 commit 6329df5

File tree

5 files changed

+73
-51
lines changed

5 files changed

+73
-51
lines changed

qga/commands-common.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* QEMU Guest Agent common/cross-platform common commands
3+
*
4+
* Copyright (c) 2020 Red Hat, Inc.
5+
*
6+
* This work is licensed under the terms of the GNU GPL, version 2 or later.
7+
* See the COPYING file in the top-level directory.
8+
*/
9+
#ifndef QGA_COMMANDS_COMMON_H
10+
#define QGA_COMMANDS_COMMON_H
11+
12+
#include "qga-qapi-types.h"
13+
14+
typedef struct GuestFileHandle GuestFileHandle;
15+
16+
GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp);
17+
18+
GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh,
19+
int64_t count, Error **errp);
20+
21+
#endif

qga/commands-posix.c

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "qemu/sockets.h"
2727
#include "qemu/base64.h"
2828
#include "qemu/cutils.h"
29+
#include "commands-common.h"
2930

3031
#ifdef HAVE_UTMPX
3132
#include <utmpx.h>
@@ -237,12 +238,12 @@ typedef enum {
237238
RW_STATE_WRITING,
238239
} RwState;
239240

240-
typedef struct GuestFileHandle {
241+
struct GuestFileHandle {
241242
uint64_t id;
242243
FILE *fh;
243244
RwState state;
244245
QTAILQ_ENTRY(GuestFileHandle) next;
245-
} GuestFileHandle;
246+
};
246247

247248
static struct {
248249
QTAILQ_HEAD(, GuestFileHandle) filehandles;
@@ -268,7 +269,7 @@ static int64_t guest_file_handle_add(FILE *fh, Error **errp)
268269
return handle;
269270
}
270271

271-
static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
272+
GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
272273
{
273274
GuestFileHandle *gfh;
274275

@@ -460,29 +461,14 @@ void qmp_guest_file_close(int64_t handle, Error **errp)
460461
g_free(gfh);
461462
}
462463

463-
struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
464-
int64_t count, Error **errp)
464+
GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh,
465+
int64_t count, Error **errp)
465466
{
466-
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
467467
GuestFileRead *read_data = NULL;
468468
guchar *buf;
469-
FILE *fh;
469+
FILE *fh = gfh->fh;
470470
size_t read_count;
471471

472-
if (!gfh) {
473-
return NULL;
474-
}
475-
476-
if (!has_count) {
477-
count = QGA_READ_COUNT_DEFAULT;
478-
} else if (count < 0 || count >= UINT32_MAX) {
479-
error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
480-
count);
481-
return NULL;
482-
}
483-
484-
fh = gfh->fh;
485-
486472
/* explicitly flush when switching from writing to reading */
487473
if (gfh->state == RW_STATE_WRITING) {
488474
int ret = fflush(fh);
@@ -497,7 +483,6 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
497483
read_count = fread(buf, 1, count, fh);
498484
if (ferror(fh)) {
499485
error_setg_errno(errp, errno, "failed to read file");
500-
slog("guest-file-read failed, handle: %" PRId64, handle);
501486
} else {
502487
buf[read_count] = 0;
503488
read_data = g_new0(GuestFileRead, 1);

qga/commands-win32.c

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "qemu/queue.h"
3838
#include "qemu/host-utils.h"
3939
#include "qemu/base64.h"
40+
#include "commands-common.h"
4041

4142
#ifndef SHTDN_REASON_FLAG_PLANNED
4243
#define SHTDN_REASON_FLAG_PLANNED 0x80000000
@@ -50,11 +51,11 @@
5051

5152
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
5253

53-
typedef struct GuestFileHandle {
54+
struct GuestFileHandle {
5455
int64_t id;
5556
HANDLE fh;
5657
QTAILQ_ENTRY(GuestFileHandle) next;
57-
} GuestFileHandle;
58+
};
5859

5960
static struct {
6061
QTAILQ_HEAD(, GuestFileHandle) filehandles;
@@ -126,7 +127,7 @@ static int64_t guest_file_handle_add(HANDLE fh, Error **errp)
126127
return handle;
127128
}
128129

129-
static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
130+
GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
130131
{
131132
GuestFileHandle *gfh;
132133
QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) {
@@ -321,39 +322,19 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
321322
}
322323
}
323324

324-
GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
325-
int64_t count, Error **errp)
325+
GuestFileRead *guest_file_read_unsafe(GuestFileHandle *gfh,
326+
int64_t count, Error **errp)
326327
{
327328
GuestFileRead *read_data = NULL;
328329
guchar *buf;
329-
HANDLE fh;
330+
HANDLE fh = gfh->fh;
330331
bool is_ok;
331332
DWORD read_count;
332-
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
333-
334-
if (!gfh) {
335-
return NULL;
336-
}
337-
if (!has_count) {
338-
count = QGA_READ_COUNT_DEFAULT;
339-
} else if (count < 0 || count >= UINT32_MAX) {
340-
error_setg(errp, "value '%" PRId64
341-
"' is invalid for argument count", count);
342-
return NULL;
343-
}
344333

345-
fh = gfh->fh;
346-
buf = g_try_malloc0(count + 1);
347-
if (!buf) {
348-
error_setg(errp,
349-
"failed to allocate sufficient memory "
350-
"to complete the requested service");
351-
return NULL;
352-
}
334+
buf = g_malloc0(count + 1);
353335
is_ok = ReadFile(fh, buf, count, &read_count, NULL);
354336
if (!is_ok) {
355337
error_setg_win32(errp, GetLastError(), "failed to read file");
356-
slog("guest-file-read failed, handle %" PRId64, handle);
357338
} else {
358339
buf[read_count] = 0;
359340
read_data = g_new0(GuestFileRead, 1);

qga/commands.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,26 @@
1111
*/
1212

1313
#include "qemu/osdep.h"
14+
#include "qemu/units.h"
1415
#include "guest-agent-core.h"
1516
#include "qga-qapi-commands.h"
1617
#include "qapi/error.h"
1718
#include "qapi/qmp/qerror.h"
1819
#include "qemu/base64.h"
1920
#include "qemu/cutils.h"
2021
#include "qemu/atomic.h"
22+
#include "commands-common.h"
2123

2224
/* Maximum captured guest-exec out_data/err_data - 16MB */
2325
#define GUEST_EXEC_MAX_OUTPUT (16*1024*1024)
2426
/* Allocation and I/O buffer for reading guest-exec out_data/err_data - 4KB */
2527
#define GUEST_EXEC_IO_SIZE (4*1024)
28+
/*
29+
* Maximum file size to read - 48MB
30+
*
31+
* (48MB + Base64 3:4 overhead = JSON parser 64 MB limit)
32+
*/
33+
#define GUEST_FILE_READ_COUNT_MAX (48 * MiB)
2634

2735
/* Note: in some situations, like with the fsfreeze, logging may be
2836
* temporarilly disabled. if it is necessary that a command be able
@@ -547,3 +555,28 @@ GuestTimezone *qmp_guest_get_timezone(Error **errp)
547555
g_free(info);
548556
return NULL;
549557
}
558+
559+
GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
560+
int64_t count, Error **errp)
561+
{
562+
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
563+
GuestFileRead *read_data;
564+
565+
if (!gfh) {
566+
return NULL;
567+
}
568+
if (!has_count) {
569+
count = QGA_READ_COUNT_DEFAULT;
570+
} else if (count < 0 || count > GUEST_FILE_READ_COUNT_MAX) {
571+
error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
572+
count);
573+
return NULL;
574+
}
575+
576+
read_data = guest_file_read_unsafe(gfh, count, errp);
577+
if (!read_data) {
578+
slog("guest-file-write failed, handle: %" PRId64, handle);
579+
}
580+
581+
return read_data;
582+
}

qga/qapi-schema.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,11 +266,13 @@
266266
##
267267
# @guest-file-read:
268268
#
269-
# Read from an open file in the guest. Data will be base64-encoded
269+
# Read from an open file in the guest. Data will be base64-encoded.
270+
# As this command is just for limited, ad-hoc debugging, such as log
271+
# file access, the number of bytes to read is limited to 48 MB.
270272
#
271273
# @handle: filehandle returned by guest-file-open
272274
#
273-
# @count: maximum number of bytes to read (default is 4KB)
275+
# @count: maximum number of bytes to read (default is 4KB, maximum is 48MB)
274276
#
275277
# Returns: @GuestFileRead on success.
276278
#

0 commit comments

Comments
 (0)