copy_one_buf() is copying uninitialized stack memory to userspace due to the compiler not initializing holes in statically allocated structures. Fix it by initializing `v` with memset().
Cc: stable@vger.kernel.org Fixes: 5c7640ab6258 ("switch compat_drm_infobufs() to drm_ioctl_kernel()") Suggested-by: Dan Carpenter dan.carpenter@oracle.com Signed-off-by: Peilin Ye yepeilin.cs@gmail.com --- drivers/gpu/drm/drm_bufs.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index a0735fbc144b..f99cd4a3f951 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1349,10 +1349,14 @@ static int copy_one_buf(void *data, int count, struct drm_buf_entry *from) { struct drm_buf_info *request = data; struct drm_buf_desc __user *to = &request->list[count]; - struct drm_buf_desc v = {.count = from->buf_count, - .size = from->buf_size, - .low_mark = from->low_mark, - .high_mark = from->high_mark}; + struct drm_buf_desc v; + + memset(&v, 0, sizeof(v)); + + v.count = from->buf_count; + v.size = from->buf_size; + v.low_mark = from->low_mark; + v.high_mark = from->high_mark;
if (copy_to_user(to, &v, offsetof(struct drm_buf_desc, flags))) return -EFAULT;
On Tue, Jul 28, 2020 at 3:45 AM Peilin Ye yepeilin.cs@gmail.com wrote:
copy_one_buf() is copying uninitialized stack memory to userspace due to the compiler not initializing holes in statically allocated structures. Fix it by initializing `v` with memset().
I would add 'potentially' somewhere in that description: it is architecture dependent whether there are holes in this structure as 'enum' types and 'long' are both dependent on the ABI, and even if there is a hole, it is undefined behavior whether the hold gets initialized.
Other than that, the patch looks good.
Cc: stable@vger.kernel.org Fixes: 5c7640ab6258 ("switch compat_drm_infobufs() to drm_ioctl_kernel()") Suggested-by: Dan Carpenter dan.carpenter@oracle.com Signed-off-by: Peilin Ye yepeilin.cs@gmail.com
Acked-by: Arnd Bergmann arnd@arndb.de
drivers/gpu/drm/drm_bufs.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index a0735fbc144b..f99cd4a3f951 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1349,10 +1349,14 @@ static int copy_one_buf(void *data, int count, struct drm_buf_entry *from) { struct drm_buf_info *request = data; struct drm_buf_desc __user *to = &request->list[count];
struct drm_buf_desc v = {.count = from->buf_count,
.size = from->buf_size,
.low_mark = from->low_mark,
.high_mark = from->high_mark};
struct drm_buf_desc v;
memset(&v, 0, sizeof(v));
v.count = from->buf_count;
v.size = from->buf_size;
v.low_mark = from->low_mark;
v.high_mark = from->high_mark; if (copy_to_user(to, &v, offsetof(struct drm_buf_desc, flags))) return -EFAULT;
-- 2.25.1
On Tue, Jul 28, 2020 at 10:11:09AM +0200, Arnd Bergmann wrote:
On Tue, Jul 28, 2020 at 3:45 AM Peilin Ye yepeilin.cs@gmail.com wrote:
copy_one_buf() is copying uninitialized stack memory to userspace due to the compiler not initializing holes in statically allocated structures. Fix it by initializing `v` with memset().
I would add 'potentially' somewhere in that description: it is architecture dependent whether there are holes in this structure as 'enum' types and 'long' are both dependent on the ABI, and even if there is a hole, it is undefined behavior whether the hold gets initialized.
I see. I will fix that up. Thank you for the advice!
Peilin Ye
Other than that, the patch looks good.
Cc: stable@vger.kernel.org Fixes: 5c7640ab6258 ("switch compat_drm_infobufs() to drm_ioctl_kernel()") Suggested-by: Dan Carpenter dan.carpenter@oracle.com Signed-off-by: Peilin Ye yepeilin.cs@gmail.com
Acked-by: Arnd Bergmann arnd@arndb.de
Currently `struct drm_buf_desc` is defined as follows:
struct drm_buf_desc { int count; int size; int low_mark; int high_mark; enum { _DRM_PAGE_ALIGN = 0x01, _DRM_AGP_BUFFER = 0x02, _DRM_SG_BUFFER = 0x04, _DRM_FB_BUFFER = 0x08, _DRM_PCI_BUFFER_RO = 0x10 } flags; unsigned long agp_start; };
copy_one_buf() is potentially copying uninitialized kernel stack memory to userspace, since the compiler may leave such "holes" (around `.flags` and `.agp_start` fields) in this statically allocated structure. Prevent it by initializing `v` with memset().
Cc: stable@vger.kernel.org Fixes: 5c7640ab6258 ("switch compat_drm_infobufs() to drm_ioctl_kernel()") Suggested-by: Dan Carpenter dan.carpenter@oracle.com Acked-by: Arnd Bergmann arnd@arndb.de Signed-off-by: Peilin Ye yepeilin.cs@gmail.com --- Change in v2: - Improve commit description. (Suggested by Arnd Bergmann arnd@arndb.de)
drivers/gpu/drm/drm_bufs.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index a0735fbc144b..f99cd4a3f951 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1349,10 +1349,14 @@ static int copy_one_buf(void *data, int count, struct drm_buf_entry *from) { struct drm_buf_info *request = data; struct drm_buf_desc __user *to = &request->list[count]; - struct drm_buf_desc v = {.count = from->buf_count, - .size = from->buf_size, - .low_mark = from->low_mark, - .high_mark = from->high_mark}; + struct drm_buf_desc v; + + memset(&v, 0, sizeof(v)); + + v.count = from->buf_count; + v.size = from->buf_size; + v.low_mark = from->low_mark; + v.high_mark = from->high_mark;
if (copy_to_user(to, &v, offsetof(struct drm_buf_desc, flags))) return -EFAULT;
From: Peilin Ye
Sent: 28 July 2020 12:52 Currently `struct drm_buf_desc` is defined as follows:
struct drm_buf_desc { int count; int size; int low_mark; int high_mark; enum { _DRM_PAGE_ALIGN = 0x01, _DRM_AGP_BUFFER = 0x02, _DRM_SG_BUFFER = 0x04, _DRM_FB_BUFFER = 0x08, _DRM_PCI_BUFFER_RO = 0x10 } flags; unsigned long agp_start; };
copy_one_buf() is potentially copying uninitialized kernel stack memory to userspace, since the compiler may leave such "holes" (around `.flags` and `.agp_start` fields) in this statically allocated structure. Prevent it by initializing `v` with memset().
Cc: stable@vger.kernel.org Fixes: 5c7640ab6258 ("switch compat_drm_infobufs() to drm_ioctl_kernel()") Suggested-by: Dan Carpenter dan.carpenter@oracle.com Acked-by: Arnd Bergmann arnd@arndb.de Signed-off-by: Peilin Ye yepeilin.cs@gmail.com
Change in v2: - Improve commit description. (Suggested by Arnd Bergmann arnd@arndb.de)
drivers/gpu/drm/drm_bufs.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index a0735fbc144b..f99cd4a3f951 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1349,10 +1349,14 @@ static int copy_one_buf(void *data, int count, struct drm_buf_entry *from) { struct drm_buf_info *request = data; struct drm_buf_desc __user *to = &request->list[count];
- struct drm_buf_desc v = {.count = from->buf_count,
.size = from->buf_size,
.low_mark = from->low_mark,
.high_mark = from->high_mark};
struct drm_buf_desc v;
memset(&v, 0, sizeof(v));
v.count = from->buf_count;
v.size = from->buf_size;
v.low_mark = from->low_mark;
v.high_mark = from->high_mark;
if (copy_to_user(to, &v, offsetof(struct drm_buf_desc, flags))) return -EFAULT;
The memset() isn't needed. The copy_to_user() stops after the 4 'int' values so no 'random' kernel stack can get copied.
Quite why it is 'right' to leave the remaining part of each userspace structure unchanged is another matter.
David.
- Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)
On Tue, Jul 28, 2020 at 12:35:17PM +0000, David Laight wrote:
From: Peilin Ye
Sent: 28 July 2020 12:52 Currently `struct drm_buf_desc` is defined as follows:
struct drm_buf_desc { int count; int size; int low_mark; int high_mark; enum { _DRM_PAGE_ALIGN = 0x01, _DRM_AGP_BUFFER = 0x02, _DRM_SG_BUFFER = 0x04, _DRM_FB_BUFFER = 0x08, _DRM_PCI_BUFFER_RO = 0x10 } flags; unsigned long agp_start; };
copy_one_buf() is potentially copying uninitialized kernel stack memory to userspace, since the compiler may leave such "holes" (around `.flags` and `.agp_start` fields) in this statically allocated structure. Prevent it by initializing `v` with memset().
Cc: stable@vger.kernel.org Fixes: 5c7640ab6258 ("switch compat_drm_infobufs() to drm_ioctl_kernel()") Suggested-by: Dan Carpenter dan.carpenter@oracle.com Acked-by: Arnd Bergmann arnd@arndb.de Signed-off-by: Peilin Ye yepeilin.cs@gmail.com
Change in v2: - Improve commit description. (Suggested by Arnd Bergmann arnd@arndb.de)
drivers/gpu/drm/drm_bufs.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index a0735fbc144b..f99cd4a3f951 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1349,10 +1349,14 @@ static int copy_one_buf(void *data, int count, struct drm_buf_entry *from) { struct drm_buf_info *request = data; struct drm_buf_desc __user *to = &request->list[count];
- struct drm_buf_desc v = {.count = from->buf_count,
.size = from->buf_size,
.low_mark = from->low_mark,
.high_mark = from->high_mark};
struct drm_buf_desc v;
memset(&v, 0, sizeof(v));
v.count = from->buf_count;
v.size = from->buf_size;
v.low_mark = from->low_mark;
v.high_mark = from->high_mark;
if (copy_to_user(to, &v, offsetof(struct drm_buf_desc, flags))) return -EFAULT;
The memset() isn't needed. The copy_to_user() stops after the 4 'int' values so no 'random' kernel stack can get copied.
You are right, I overlooked that. Thank you for pointing this out!
Peilin Ye
Quite why it is 'right' to leave the remaining part of each userspace structure unchanged is another matter.
David.
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)
dri-devel@lists.freedesktop.org