There is a time-of-check-to-time-of-use error in drm_getunique() due to retrieving file_priv->master prior to locking the device's master mutex.
An example can be seen in the crash report of the use-after-free error found by Syzbot: https://syzkaller.appspot.com/bug?id=148d2f1dfac64af52ffd27b661981a540724f80...
In the report, the master pointer was used after being freed. This is because another process had acquired the device's master mutex in drm_setmaster_ioctl(), then overwrote fpriv->master in drm_new_set_master(). The old value of fpriv->master was subsequently freed before the mutex was unlocked.
To fix this, we lock the device's master mutex before retrieving the pointer from from fpriv->master. This patch passes the Syzbot reproducer test.
Reported-by: syzbot+c3a706cec1ea99e1c693@syzkaller.appspotmail.com Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com ---
In this patch, I made the assumption that dev and file_priv->master->dev refer to the same struct drm_device*, and from my test runs this seemed to be the case. If this was a wrong assumption to make, please let me know.
drivers/gpu/drm/drm_ioctl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index d273d1a8603a..495a4767a443 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -118,17 +118,18 @@ int drm_getunique(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_unique *u = data; - struct drm_master *master = file_priv->master; + struct drm_master *master;
- mutex_lock(&master->dev->master_mutex); + mutex_lock(&dev->master_mutex); + master = file_priv->master; if (u->unique_len >= master->unique_len) { if (copy_to_user(u->unique, master->unique, master->unique_len)) { - mutex_unlock(&master->dev->master_mutex); + mutex_unlock(&dev->master_mutex); return -EFAULT; } } u->unique_len = master->unique_len; - mutex_unlock(&master->dev->master_mutex); + mutex_unlock(&dev->master_mutex);
return 0; }
On Tue, Jun 08, 2021 at 07:04:36PM +0800, Desmond Cheong Zhi Xi wrote:
There is a time-of-check-to-time-of-use error in drm_getunique() due to retrieving file_priv->master prior to locking the device's master mutex.
An example can be seen in the crash report of the use-after-free error found by Syzbot: https://syzkaller.appspot.com/bug?id=148d2f1dfac64af52ffd27b661981a540724f80...
In the report, the master pointer was used after being freed. This is because another process had acquired the device's master mutex in drm_setmaster_ioctl(), then overwrote fpriv->master in drm_new_set_master(). The old value of fpriv->master was subsequently freed before the mutex was unlocked.
To fix this, we lock the device's master mutex before retrieving the pointer from from fpriv->master. This patch passes the Syzbot reproducer test.
Reported-by: syzbot+c3a706cec1ea99e1c693@syzkaller.appspotmail.com Signed-off-by: Desmond Cheong Zhi Xi desmondcheongzx@gmail.com
Very nice debug work, thanks a lot!
In this patch, I made the assumption that dev and file_priv->master->dev refer to the same struct drm_device*, and from my test runs this seemed to be the case. If this was a wrong assumption to make, please let me know.
Yup that's correct.
Patch applied to drm-misc-fixes and cc: stable added so it gets backported.
Cheers, Daniel
drivers/gpu/drm/drm_ioctl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index d273d1a8603a..495a4767a443 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -118,17 +118,18 @@ int drm_getunique(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_unique *u = data;
- struct drm_master *master = file_priv->master;
- struct drm_master *master;
- mutex_lock(&master->dev->master_mutex);
- mutex_lock(&dev->master_mutex);
- master = file_priv->master; if (u->unique_len >= master->unique_len) { if (copy_to_user(u->unique, master->unique, master->unique_len)) {
mutex_unlock(&master->dev->master_mutex);
} } u->unique_len = master->unique_len;mutex_unlock(&dev->master_mutex); return -EFAULT;
- mutex_unlock(&master->dev->master_mutex);
mutex_unlock(&dev->master_mutex);
return 0;
}
2.25.1
dri-devel@lists.freedesktop.org