[gvfs] mtp: Handle read-past-EOF in GetPartialObject(64) ourselves



commit 08612cd495fd3a10e83041dd2245385d05a24e07
Author: Philip Langdale <philipl overt org>
Date:   Fri Nov 10 07:59:42 2017 -0800

    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

 daemon/gvfsbackendmtp.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)
---
diff --git a/daemon/gvfsbackendmtp.c b/daemon/gvfsbackendmtp.c
index 2a418a2..a606ec2 100644
--- a/daemon/gvfsbackendmtp.c
+++ b/daemon/gvfsbackendmtp.c
@@ -2444,6 +2444,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);
@@ -2464,6 +2479,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));


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]