Skip to content

Commit b8f0031

Browse files
committed
Avoid allocating a big buffer for each loop iteration
There's no reason to allocate a big buffer for each loop iteration, only to free it immediately afterwards. Just allocate the buffer once and reuse it. This breaks the libqrexec ABI, but the changed functions are not used outside of libqrexec itself. To ensure that any problems are caught early, move the functions to a separate header and mark them as having hidden visibility.
1 parent 57855c6 commit b8f0031

File tree

5 files changed

+125
-72
lines changed

5 files changed

+125
-72
lines changed

fuzz/qrexec_remote_fuzzer.c

+16-6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <stdint.h>
66

77
#include "libqrexec-utils.h"
8+
#include "remote.h"
89
#include "fuzz.h"
910

1011
void _Noreturn fuzz_exit(int status) {
@@ -13,7 +14,17 @@ void _Noreturn fuzz_exit(int status) {
1314

1415
void LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
1516
fuzz_file_t *vchan_file, *stdin_file, *local_stderr_file;
16-
struct buffer stdin_buf;
17+
const size_t max_chunk_size = max_data_chunk_size(QREXEC_PROTOCOL_V2);
18+
struct buffer stdin_buf = {
19+
.data = NULL,
20+
.buflen = 0,
21+
};
22+
struct buffer remote_buf = {
23+
.data = malloc(max_chunk_size),
24+
.buflen = max_chunk_size,
25+
};
26+
if (remote_buf.data == NULL)
27+
abort();
1728
int status;
1829

1930
stdin_file = fuzz_file_create(0, NULL, 0);
@@ -23,14 +34,13 @@ void LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
2334
stdin_file->open_read = false;
2435
local_stderr_file->open_read = false;
2536

26-
buffer_init(&stdin_buf);
27-
28-
handle_remote_data(
37+
handle_remote_data_v2(
2938
vchan_file, stdin_file->fd, &status,
30-
&stdin_buf, QREXEC_PROTOCOL_V2,
31-
false, true, false);
39+
&stdin_buf, false, true, (bool)false,
40+
&remote_buf);
3241

3342
fuzz_file_destroy(stdin_file);
3443
fuzz_file_destroy(vchan_file);
3544
fuzz_file_destroy(local_stderr_file);
45+
free(remote_buf.data);
3646
}

libqrexec/libqrexec-utils.h

-35
Original file line numberDiff line numberDiff line change
@@ -229,46 +229,11 @@ void do_replace_chars(char *buf, int len);
229229
#define REMOTE_EOF 0
230230
#define REMOTE_OK 1
231231

232-
/*
233-
* Handle data from vchan. Sends MSG_DATA_STDIN and MSG_DATA_STDOUT to
234-
* specified FD (unless it's -1), and MSG_DATA_STDERR to our stderr.
235-
*
236-
* Return codes:
237-
* REMOTE_EXITED - remote process terminated, do not send more data to it
238-
* ("status" will be set)
239-
* REMOTE_ERROR - vchan error occured
240-
* REMOTE_EOF - EOF received, do not access this FD again
241-
* REMOTE_OK - maybe some data processed, call again when buffer space and
242-
* more data available
243-
*
244-
* Options:
245-
* replace_chars_stdout, replace_chars_stderr - remove non-printable
246-
* characters from stdout/stderr
247-
*/
248-
int handle_remote_data(
249-
libvchan_t *data_vchan, int stdin_fd, int *status,
250-
struct buffer *stdin_buf, int data_protocol_version,
251-
bool replace_chars_stdout, bool replace_chars_stderr, bool is_service);
252-
253232
struct prefix_data {
254233
const char *data;
255234
size_t len;
256235
};
257236

258-
/*
259-
* Handle data from the specified FD (cannot be -1) and send it over vchan
260-
* with a given message type (MSG_DATA_STDIN/STDOUT/STDERR).
261-
*
262-
* Return codes:
263-
* REMOTE_ERROR - vchan error occured
264-
* REMOTE_EOF - EOF received, do not access this FD again
265-
* REMOTE_OK - some data processed, call it again when buffer space and
266-
* more data availabla
267-
*/
268-
int handle_input(
269-
libvchan_t *vchan, int fd, int msg_type,
270-
int data_protocol_version, struct prefix_data *data);
271-
272237
int send_exit_code(libvchan_t *vchan, int status);
273238

274239
/* Set of options for process_io(). */

libqrexec/process_io.c

+26-9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <unistd.h>
3131

3232
#include "libqrexec-utils.h"
33+
#include "remote.h"
3334

3435
static _Noreturn void handle_vchan_error(const char *op)
3536
{
@@ -107,8 +108,8 @@ int process_io(const struct process_io_request *req) {
107108
bool is_service = req->is_service;
108109
bool replace_chars_stdout = req->replace_chars_stdout;
109110
bool replace_chars_stderr = req->replace_chars_stderr;
110-
int data_protocol_version = req->data_protocol_version;
111-
111+
const int data_protocol_version = req->data_protocol_version;
112+
const size_t max_chunk_size = max_data_chunk_size(data_protocol_version);
112113
pid_t local_pid = req->local_pid;
113114
volatile sig_atomic_t *sigchld = req->sigchld;
114115
volatile sig_atomic_t *sigusr1 = req->sigusr1;
@@ -125,6 +126,19 @@ int process_io(const struct process_io_request *req) {
125126
struct timespec normal_timeout = { 10, 0 };
126127
struct prefix_data empty = { 0, 0 }, prefix = req->prefix_data;
127128

129+
struct buffer remote_buffer = {
130+
.data = malloc(max_chunk_size),
131+
.buflen = max_chunk_size,
132+
};
133+
if (remote_buffer.data == NULL)
134+
handle_vchan_error("remote buffer alloc");
135+
struct buffer stdin_buffer = {
136+
.data = malloc(max_chunk_size),
137+
.buflen = max_chunk_size,
138+
};
139+
if (stdin_buffer.data == NULL)
140+
handle_vchan_error("stdin buffer alloc");
141+
128142
sigemptyset(&pollmask);
129143
sigaddset(&pollmask, SIGCHLD);
130144
sigprocmask(SIG_BLOCK, &pollmask, NULL);
@@ -269,14 +283,14 @@ int process_io(const struct process_io_request *req) {
269283
}
270284

271285
/* handle_remote_data will check if any data is available */
272-
switch (handle_remote_data(
286+
switch (handle_remote_data_v2(
273287
vchan, stdin_fd,
274288
&remote_status,
275289
stdin_buf,
276-
data_protocol_version,
277290
replace_chars_stdout > 0,
278291
replace_chars_stderr > 0,
279-
is_service)) {
292+
is_service,
293+
&remote_buffer)) {
280294
case REMOTE_ERROR:
281295
handle_vchan_error("read");
282296
break;
@@ -296,9 +310,9 @@ int process_io(const struct process_io_request *req) {
296310
break;
297311
}
298312
if (prefix.len > 0 || (stdout_fd >= 0 && fds[FD_STDOUT].revents)) {
299-
switch (handle_input(
313+
switch (handle_input_v2(
300314
vchan, stdout_fd, stdout_msg_type,
301-
data_protocol_version, &prefix)) {
315+
&prefix, &stdin_buffer)) {
302316
case REMOTE_ERROR:
303317
handle_vchan_error("send(handle_input stdout)");
304318
break;
@@ -309,9 +323,9 @@ int process_io(const struct process_io_request *req) {
309323
}
310324
}
311325
if (stderr_fd >= 0 && fds[FD_STDERR].revents) {
312-
switch (handle_input(
326+
switch (handle_input_v2(
313327
vchan, stderr_fd, MSG_DATA_STDERR,
314-
data_protocol_version, &empty)) {
328+
&empty, &stdin_buffer)) {
315329
case REMOTE_ERROR:
316330
handle_vchan_error("send(handle_input stderr)");
317331
break;
@@ -340,6 +354,9 @@ int process_io(const struct process_io_request *req) {
340354
PERROR("waitpid");
341355
}
342356

357+
free(remote_buffer.data);
358+
free(stdin_buffer.data);
359+
343360
if (!is_service && remote_status)
344361
return remote_status;
345362
return local_pid ? local_status : 0;

libqrexec/remote.c

+19-22
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,25 @@
3030
#include <assert.h>
3131

3232
#include "libqrexec-utils.h"
33+
#include "remote.h"
3334

34-
int handle_remote_data(
35+
int handle_remote_data_v2(
3536
libvchan_t *data_vchan, int stdin_fd, int *status,
36-
struct buffer *stdin_buf, int data_protocol_version,
37-
bool replace_chars_stdout, bool replace_chars_stderr, bool is_service)
37+
struct buffer *stdin_buf,
38+
bool replace_chars_stdout,
39+
bool replace_chars_stderr,
40+
bool is_service,
41+
const struct buffer *buffer)
3842
{
3943
struct msg_header hdr;
40-
const size_t max_len = max_data_chunk_size(data_protocol_version);
41-
char *buf;
44+
const size_t max_len = (size_t)buffer->buflen;
45+
char *buf = buffer->data;
4246
int rc = REMOTE_ERROR;
4347
bool msg_data_warned = false;
4448

49+
if (buffer->buflen < 0)
50+
abort();
51+
4552
/* do not receive any data if we have something already buffered */
4653
switch (flush_client_data(stdin_fd, stdin_buf)) {
4754
case WRITE_STDIN_OK:
@@ -53,12 +60,6 @@ int handle_remote_data(
5360
return REMOTE_EOF;
5461
}
5562

56-
buf = malloc(max_len);
57-
if (!buf) {
58-
PERROR("malloc");
59-
return REMOTE_ERROR;
60-
}
61-
6263
while (libvchan_data_ready(data_vchan) > 0) {
6364
if (libvchan_recv(data_vchan, &hdr, sizeof(hdr)) != sizeof(hdr))
6465
goto out;
@@ -143,25 +144,22 @@ int handle_remote_data(
143144
}
144145
rc = REMOTE_OK;
145146
out:
146-
free(buf);
147147
return rc;
148148
}
149149

150-
int handle_input(
150+
int handle_input_v2(
151151
libvchan_t *vchan, int fd, int msg_type,
152-
int data_protocol_version, struct prefix_data *prefix_data)
152+
struct prefix_data *prefix_data,
153+
const struct buffer *buffer)
153154
{
154-
const size_t max_len = max_data_chunk_size(data_protocol_version);
155-
char *buf;
155+
const size_t max_len = (size_t)buffer->buflen;
156+
char *buf = buffer->data;
156157
ssize_t len;
157158
struct msg_header hdr;
158159
int rc = REMOTE_ERROR, buf_space;
159160

160-
buf = malloc(max_len);
161-
if (!buf) {
162-
PERROR("malloc");
163-
return REMOTE_ERROR;
164-
}
161+
if (buffer->buflen < 0)
162+
abort();
165163

166164
static_assert(SSIZE_MAX >= INT_MAX, "can't happen on Linux");
167165
hdr.type = msg_type;
@@ -204,7 +202,6 @@ int handle_input(
204202
}
205203
rc = REMOTE_OK;
206204
out:
207-
free(buf);
208205
return rc;
209206
}
210207

libqrexec/remote.h

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* The Qubes OS Project, http://www.qubes-os.org
3+
*
4+
* Copyright (C) 2010 Rafal Wojtczuk <[email protected]>
5+
* Copyright (C) 2013 Marek Marczykowski <[email protected]>
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU General Public License
9+
* as published by the Free Software Foundation; either version 2
10+
* of the License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20+
*
21+
*/
22+
23+
#include <stdbool.h>
24+
#include <libvchan.h>
25+
26+
#pragma GCC visibility push(hidden)
27+
28+
/*
29+
* Handle data from vchan. Sends MSG_DATA_STDIN and MSG_DATA_STDOUT to
30+
* specified FD (unless it's -1), and MSG_DATA_STDERR to our stderr.
31+
*
32+
* Return codes:
33+
* REMOTE_EXITED - remote process terminated, do not send more data to it
34+
* ("status" will be set)
35+
* REMOTE_ERROR - vchan error occured
36+
* REMOTE_EOF - EOF received, do not access this FD again
37+
* REMOTE_OK - maybe some data processed, call again when buffer space and
38+
* more data available
39+
*
40+
* Options:
41+
* replace_chars_stdout, replace_chars_stderr - remove non-printable
42+
* characters from stdout/stderr
43+
*/
44+
int handle_remote_data_v2(
45+
libvchan_t *data_vchan, int stdin_fd, int *status,
46+
struct buffer *stdin_buf,
47+
bool replace_chars_stdout, bool replace_chars_stderr, bool is_service,
48+
const struct buffer *buffer);
49+
50+
/*
51+
* Handle data from the specified FD (cannot be -1) and send it over vchan
52+
* with a given message type (MSG_DATA_STDIN/STDOUT/STDERR).
53+
*
54+
* Return codes:
55+
* REMOTE_ERROR - vchan error occured
56+
* REMOTE_EOF - EOF received, do not access this FD again
57+
* REMOTE_OK - some data processed, call it again when buffer space and
58+
* more data availabla
59+
*/
60+
int handle_input_v2(
61+
libvchan_t *vchan, int fd, int msg_type,
62+
struct prefix_data *prefix_data,
63+
const struct buffer *buffer);
64+
#pragma GCC visibility pop

0 commit comments

Comments
 (0)