gfile: Rewrite load_contents() to use realloc()

GByteArray is limited to 4GB in size and the current code silently
overflows when that happens.

Replace both load_contents() and load_contents_async() implementations
with a version that uses realloc() and gsize to maintain the array.
This commit is contained in:
Benjamin Otte 2023-04-12 21:34:34 +02:00 committed by Philip Withnall
parent a57f0b190a
commit e8254f14fd

View File

@ -8086,7 +8086,8 @@ g_file_load_contents (GFile *file,
GError **error) GError **error)
{ {
GFileInputStream *in; GFileInputStream *in;
GByteArray *content; char *data;
gsize size;
gsize pos; gsize pos;
gssize res; gssize res;
GFileInfo *info; GFileInfo *info;
@ -8098,17 +8099,22 @@ g_file_load_contents (GFile *file,
if (in == NULL) if (in == NULL)
return FALSE; return FALSE;
content = g_byte_array_new (); size = GET_CONTENT_BLOCK_SIZE;
data = g_malloc (GET_CONTENT_BLOCK_SIZE);
pos = 0; pos = 0;
g_byte_array_set_size (content, pos + GET_CONTENT_BLOCK_SIZE + 1);
while ((res = g_input_stream_read (G_INPUT_STREAM (in), while ((res = g_input_stream_read (G_INPUT_STREAM (in),
content->data + pos, data + pos,
GET_CONTENT_BLOCK_SIZE, GET_CONTENT_BLOCK_SIZE,
cancellable, error)) > 0) cancellable, error)) > 0)
{ {
pos += res; pos += res;
g_byte_array_set_size (content, pos + GET_CONTENT_BLOCK_SIZE + 1); if (size - pos < GET_CONTENT_BLOCK_SIZE)
{
g_assert (size <= G_MAXSIZE / 2);
size *= 2;
data = g_realloc (data, size);
}
} }
if (etag_out) if (etag_out)
@ -8133,17 +8139,19 @@ g_file_load_contents (GFile *file,
if (res < 0) if (res < 0)
{ {
/* error is set already */ /* error is set already */
g_byte_array_free (content, TRUE); g_free (data);
return FALSE; return FALSE;
} }
if (length) if (length)
*length = pos; *length = pos;
/* Zero terminate (we got an extra byte allocated for this */ /* Zero terminate (allocating extra bytes if needed) */
content->data[pos] = 0; if (pos >= size)
data = g_realloc (data, pos + 1);
data[pos] = 0;
*contents = (char *)g_byte_array_free (content, FALSE); *contents = g_steal_pointer (&data);
return TRUE; return TRUE;
} }
@ -8151,7 +8159,8 @@ g_file_load_contents (GFile *file,
typedef struct { typedef struct {
GTask *task; GTask *task;
GFileReadMoreCallback read_more_callback; GFileReadMoreCallback read_more_callback;
GByteArray *content; char *data;
gsize size;
gsize pos; gsize pos;
char *etag; char *etag;
} LoadContentsData; } LoadContentsData;
@ -8160,12 +8169,31 @@ typedef struct {
static void static void
load_contents_data_free (LoadContentsData *data) load_contents_data_free (LoadContentsData *data)
{ {
if (data->content) g_clear_pointer (&data->data, g_free);
g_byte_array_free (data->content, TRUE);
g_free (data->etag); g_free (data->etag);
g_free (data); g_free (data);
} }
static void
load_contents_data_ensure_space (LoadContentsData *data,
gsize space)
{
if (data->size - data->pos < space)
{
if (data->data == NULL)
{
data->size = space;
data->data = g_malloc (space);
}
else
{
g_assert (data->size <= G_MAXSIZE / 2);
data->size *= 2;
data->data = g_realloc (data->data, data->size);
}
}
}
static void static void
load_contents_close_callback (GObject *obj, load_contents_close_callback (GObject *obj,
GAsyncResult *close_res, GAsyncResult *close_res,
@ -8238,12 +8266,10 @@ load_contents_read_callback (GObject *obj,
{ {
data->pos += read_size; data->pos += read_size;
g_byte_array_set_size (data->content, load_contents_data_ensure_space (data, GET_CONTENT_BLOCK_SIZE);
data->pos + GET_CONTENT_BLOCK_SIZE);
if (data->read_more_callback && if (data->read_more_callback &&
!data->read_more_callback ((char *)data->content->data, data->pos, !data->read_more_callback (data->data, data->pos,
g_async_result_get_user_data (G_ASYNC_RESULT (data->task)))) g_async_result_get_user_data (G_ASYNC_RESULT (data->task))))
g_file_input_stream_query_info_async (G_FILE_INPUT_STREAM (stream), g_file_input_stream_query_info_async (G_FILE_INPUT_STREAM (stream),
G_FILE_ATTRIBUTE_ETAG_VALUE, G_FILE_ATTRIBUTE_ETAG_VALUE,
@ -8253,7 +8279,7 @@ load_contents_read_callback (GObject *obj,
data); data);
else else
g_input_stream_read_async (stream, g_input_stream_read_async (stream,
data->content->data + data->pos, data->data + data->pos,
GET_CONTENT_BLOCK_SIZE, GET_CONTENT_BLOCK_SIZE,
0, 0,
g_task_get_cancellable (data->task), g_task_get_cancellable (data->task),
@ -8276,10 +8302,9 @@ load_contents_open_callback (GObject *obj,
if (stream) if (stream)
{ {
g_byte_array_set_size (data->content, load_contents_data_ensure_space (data, GET_CONTENT_BLOCK_SIZE);
data->pos + GET_CONTENT_BLOCK_SIZE);
g_input_stream_read_async (G_INPUT_STREAM (stream), g_input_stream_read_async (G_INPUT_STREAM (stream),
data->content->data + data->pos, data->data + data->pos,
GET_CONTENT_BLOCK_SIZE, GET_CONTENT_BLOCK_SIZE,
0, 0,
g_task_get_cancellable (data->task), g_task_get_cancellable (data->task),
@ -8329,7 +8354,6 @@ g_file_load_partial_contents_async (GFile *file,
data = g_new0 (LoadContentsData, 1); data = g_new0 (LoadContentsData, 1);
data->read_more_callback = read_more_callback; data->read_more_callback = read_more_callback;
data->content = g_byte_array_new ();
data->task = g_task_new (file, cancellable, callback, user_data); data->task = g_task_new (file, cancellable, callback, user_data);
g_task_set_source_tag (data->task, g_file_load_partial_contents_async); g_task_set_source_tag (data->task, g_file_load_partial_contents_async);
@ -8398,11 +8422,10 @@ g_file_load_partial_contents_finish (GFile *file,
} }
/* Zero terminate */ /* Zero terminate */
g_byte_array_set_size (data->content, data->pos + 1); load_contents_data_ensure_space (data, 1);
data->content->data[data->pos] = 0; data->data[data->pos] = 0;
*contents = (char *)g_byte_array_free (data->content, FALSE); *contents = g_steal_pointer (&data->data);
data->content = NULL;
return TRUE; return TRUE;
} }