mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 03:16:17 +01:00
gfileutils: Fix g_file_get_contents() silent under-read of large files
On certain platforms where file size (off_t) can be truncated when assigning to gsize, get_contents_regfile() may use the truncated size as the size to read. Add an explicit check to raise an error in such cases. G_FILE_ERROR_FAILED will be raised in this case, aligning with behavior for other cases. This generally affects 32-bit non-Win32 platforms.
This commit is contained in:
parent
c7a02280f5
commit
cf5e371c67
@ -750,9 +750,9 @@ get_contents_stdio (const gchar *filename,
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
G_FILE_ERROR_NOMEM,
|
||||
g_dngettext (GETTEXT_PACKAGE, "Could not allocate %lu byte to read file “%s”", "Could not allocate %lu bytes to read file “%s”", (gulong)total_allocated),
|
||||
(gulong) total_allocated,
|
||||
display_filename);
|
||||
g_dngettext (GETTEXT_PACKAGE, "Could not allocate %" G_GSIZE_MODIFIER "u byte to read file “%s”", "Could not allocate %" G_GSIZE_MODIFIER "u bytes to read file “%s”", total_allocated),
|
||||
total_allocated,
|
||||
display_filename);
|
||||
g_free (display_filename);
|
||||
|
||||
goto error;
|
||||
@ -830,7 +830,19 @@ get_contents_regfile (const gchar *filename,
|
||||
gsize size;
|
||||
gsize alloc_size;
|
||||
gchar *display_filename;
|
||||
|
||||
|
||||
if ((G_MAXOFFSET >= G_MAXSIZE) && (stat_buf->st_size > (goffset) (G_MAXSIZE - 1)))
|
||||
{
|
||||
display_filename = g_filename_display_name (filename);
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
G_FILE_ERROR_FAILED,
|
||||
_("File “%s” is too large"),
|
||||
display_filename);
|
||||
g_free (display_filename);
|
||||
goto error;
|
||||
}
|
||||
|
||||
size = stat_buf->st_size;
|
||||
|
||||
alloc_size = size + 1;
|
||||
@ -842,9 +854,9 @@ get_contents_regfile (const gchar *filename,
|
||||
g_set_error (error,
|
||||
G_FILE_ERROR,
|
||||
G_FILE_ERROR_NOMEM,
|
||||
g_dngettext (GETTEXT_PACKAGE, "Could not allocate %lu byte to read file “%s”", "Could not allocate %lu bytes to read file “%s”", (gulong)alloc_size),
|
||||
(gulong) alloc_size,
|
||||
display_filename);
|
||||
g_dngettext (GETTEXT_PACKAGE, "Could not allocate %" G_GSIZE_MODIFIER "u byte to read file “%s”", "Could not allocate %" G_GSIZE_MODIFIER "u bytes to read file “%s”", alloc_size),
|
||||
alloc_size,
|
||||
display_filename);
|
||||
g_free (display_filename);
|
||||
goto error;
|
||||
}
|
||||
@ -891,7 +903,7 @@ get_contents_regfile (const gchar *filename,
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
error:
|
||||
|
||||
close (fd);
|
||||
|
||||
|
@ -1474,6 +1474,121 @@ test_get_contents (void)
|
||||
g_remove (filename);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
resize_file (const gchar *filename,
|
||||
gint64 size)
|
||||
{
|
||||
int fd;
|
||||
int retval;
|
||||
|
||||
fd = g_open (filename, O_CREAT | O_RDWR | O_TRUNC, 0666);
|
||||
g_assert_cmpint (fd, >=, 0);
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
retval = _chsize_s (fd, size);
|
||||
#else
|
||||
retval = ftruncate64 (fd, size);
|
||||
#endif
|
||||
if (retval != 0)
|
||||
{
|
||||
g_test_message ("Error trying to resize file (%s)", strerror (errno));
|
||||
close (fd);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
close (fd);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_error_in_list (GFileError error_code,
|
||||
const GFileError ok_list[],
|
||||
size_t ok_count)
|
||||
{
|
||||
for (size_t i = 0; i < ok_count; i++)
|
||||
{
|
||||
if (ok_list[i] == error_code)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
get_largefile_check_len (const gchar *filename,
|
||||
gint64 large_len,
|
||||
const GFileError ok_list[],
|
||||
size_t ok_count)
|
||||
{
|
||||
gboolean get_ok;
|
||||
gsize len;
|
||||
gchar *contents;
|
||||
GError *error = NULL;
|
||||
|
||||
get_ok = g_file_get_contents (filename, &contents, &len, &error);
|
||||
if (get_ok)
|
||||
{
|
||||
g_assert_cmpint ((gint64) len, ==, large_len);
|
||||
g_free (contents);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_cmpint (error->domain, ==, G_FILE_ERROR);
|
||||
if (is_error_in_list ((GFileError)error->code, ok_list, ok_count))
|
||||
{
|
||||
g_test_message ("Error reading file of size 0x%" G_GINT64_MODIFIER "x, but with acceptable error type (%s)", large_len, error->message);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fail for other errors */
|
||||
g_assert_no_error (error);
|
||||
}
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_get_contents_largefile (void)
|
||||
{
|
||||
if (!g_test_slow ())
|
||||
{
|
||||
g_test_skip ("Skipping slow largefile test");
|
||||
return;
|
||||
}
|
||||
|
||||
const gchar *filename = "file-test-get-contents-large";
|
||||
gint64 large_len;
|
||||
|
||||
/* error OK if couldn't allocate large buffer, or if file is too large */
|
||||
const GFileError too_large_errors[] = { G_FILE_ERROR_NOMEM, G_FILE_ERROR_FAILED };
|
||||
/* error OK if couldn't allocate large buffer */
|
||||
const GFileError nomem_errors[] = { G_FILE_ERROR_NOMEM };
|
||||
|
||||
/* OK to fail to read this, but don't silently under-read */
|
||||
large_len = (G_GINT64_CONSTANT (1) << 32) + 16;
|
||||
if (!resize_file (filename, large_len))
|
||||
goto failed_resize;
|
||||
get_largefile_check_len (filename, large_len, too_large_errors, G_N_ELEMENTS (too_large_errors));
|
||||
|
||||
/* OK to fail to read this size, but don't silently under-read */
|
||||
large_len = (G_GINT64_CONSTANT (1) << 32) - 1;
|
||||
if (!resize_file (filename, large_len))
|
||||
goto failed_resize;
|
||||
get_largefile_check_len (filename, large_len, too_large_errors, G_N_ELEMENTS (too_large_errors));
|
||||
|
||||
/* OK to fail memory allocation, but don't otherwise fail this size */
|
||||
large_len = (G_GINT64_CONSTANT (1) << 31) - 1;
|
||||
if (!resize_file (filename, large_len))
|
||||
goto failed_resize;
|
||||
get_largefile_check_len (filename, large_len, nomem_errors, G_N_ELEMENTS (nomem_errors));
|
||||
|
||||
g_remove (filename);
|
||||
return;
|
||||
|
||||
failed_resize:
|
||||
g_test_incomplete ("Failed to resize large file, unable to complete large file tests.");
|
||||
g_remove (filename);
|
||||
}
|
||||
|
||||
static void
|
||||
test_file_test (void)
|
||||
{
|
||||
@ -2648,6 +2763,7 @@ main (int argc,
|
||||
g_test_add_func ("/fileutils/mkstemp", test_mkstemp);
|
||||
g_test_add_func ("/fileutils/mkdtemp", test_mkdtemp);
|
||||
g_test_add_func ("/fileutils/get-contents", test_get_contents);
|
||||
g_test_add_func ("/fileutils/get-contents-large-file", test_get_contents_largefile);
|
||||
g_test_add_func ("/fileutils/set-contents", test_set_contents);
|
||||
g_test_add_func ("/fileutils/set-contents-full", test_set_contents_full);
|
||||
g_test_add_func ("/fileutils/set-contents-full/read-only-file", test_set_contents_full_read_only_file);
|
||||
|
Loading…
Reference in New Issue
Block a user