summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Langdale <philipl@overt.org>2017-11-10 07:59:42 -0800
committerOndrej Holy <oholy@redhat.com>2018-03-02 16:01:42 +0100
commit9b3a05519332a4da9ea5d503edba72f2f29579ab (patch)
treeead6ba6da11aa64964d62b438141a2c0fd6781d9
parent3e995d2200bf733611fed64bd1be23d9f4a7a85c (diff)
downloadgvfs-9b3a05519332a4da9ea5d503edba72f2f29579ab.tar.gz
mtp: Handle read-past-EOF in GetPartialObject(64) ourselves
Up until very recently, the Android MTP driver did not do bounds checking on reads past EOF, leading to undefined behaviour, which includes hanging the transfer on some devices. According to Google engineers, this is fixed in the kernels used by the Pixel and Pixel 2 (and this has been verified in testing), but that basically means that every other Android device in existence has this bug, and is unlikely to ever be fixed. So, we need to enforce POSIX semantics ourselves and truncate reads past EOF. libmtp has implemented a check, but we should validate as well so that we have working behaviour without requiring a libmtp update. https://bugzilla.gnome.org/show_bug.cgi?id=784477
-rw-r--r--daemon/gvfsbackendmtp.c16
1 files changed, 16 insertions, 0 deletions
diff --git a/daemon/gvfsbackendmtp.c b/daemon/gvfsbackendmtp.c
index 0b67c408..798b7f9a 100644
--- a/daemon/gvfsbackendmtp.c
+++ b/daemon/gvfsbackendmtp.c
@@ -2456,6 +2456,21 @@ do_read (GVfsBackend *backend,
goto exit;
}
+ /*
+ * Almost all android devices have a bug where they do not enforce
+ * POSIX semantics for read past EOF, leading to undefined
+ * behaviour including device-side hangs. We'd better handle it
+ * here.
+ */
+ if (offset >= handle->size) {
+ g_debug ("(II) skipping read with offset past EOF\n");
+ actual = 0;
+ goto finished;
+ } else if (offset + bytes_requested > handle->size) {
+ g_debug ("(II) reducing bytes_requested to avoid reading past EOF\n");
+ bytes_requested = handle->size - offset;
+ }
+
unsigned char *temp;
int ret = LIBMTP_GetPartialObject (G_VFS_BACKEND_MTP (backend)->device, id, offset,
bytes_requested, &temp, &actual);
@@ -2476,6 +2491,7 @@ do_read (GVfsBackend *backend,
memcpy (buffer, bytes->data + offset, actual);
}
+ finished:
handle->offset = offset + actual;
g_vfs_job_read_set_size (job, actual);
g_vfs_job_succeeded (G_VFS_JOB (job));