gpu: host1x: Do not leak BO's phys address to userspace
PATCH 1/1 gpu: host1x: Do not leak BO's phys address to userspace
Do gathers coping before patching them, so the original gathers are left untouched. That's not as bad as leaking a kernel addresses, but still doesn't feel right.
--- drivers/gpu/host1x/job.c | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-)
drivers/gpu/host1x/job.c +30 -16
@@ -137,8 +137,9 @@ static void host1x_syncpt_patch_offset(struct host1x_syncpt *sp,* avoid a wrap condition in the HW). */ static int do_waitchks (struct host1x_job *job,struct host1x *host, -struct host1x_bo *patch) +struct host1x_job_gather *g) { +struct host1x_bo *patch = g->bo;int i;/* compare syncpt vs wait threshold */
@@ -165,7 +166,8 @@ static int do_waitchks(struct host1x_job *job, struct host1x *host, wait->syncpt_id, sp->name, wait->thresh, host1x_syncpt_read_min(sp)); -host1x_syncpt_patch_offset (sp, patch, wait->offset); +host1x_syncpt_patch_offset (sp, patch, + g->offset + wait->offset); } wait->bo =NULL ;
@@ -269,11 +271,12 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)return err; } -static int do_relocs (struct host1x_job *job,struct host1x_bo *cmdbuf) +static int do_relocs (struct host1x_job *job,struct host1x_job_gather *g) {int i =0 ;u32 last_page = ~0 ;void *cmdbuf_page_addr =NULL ; +struct host1x_bo *cmdbuf = g->bo;/* pin & patch the relocs for one gather */ for (i =0 ; i < job->num_relocs; i++) {
@@ -286,7 +289,8 @@ static int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)if (cmdbuf != reloc->cmdbuf.bo)continue ; -if (last_page != reloc->cmdbuf.offset >>PAGE_SHIFT ) { +if (!IS_ENABLED (CONFIG_TEGRA_HOST1X_FIREWALL ) && + last_page != reloc->cmdbuf.offset >>PAGE_SHIFT ) {if (cmdbuf_page_addr)host1x_bo_kunmap (cmdbuf, last_page, cmdbuf_page_addr);
@@ -301,11 +305,20 @@ static int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) } } +if (IS_ENABLED (CONFIG_TEGRA_HOST1X_FIREWALL )) { + cmdbuf_page_addr = job->gather_copy_mapped; + cmdbuf_page_addr += g->offset; + } + target = cmdbuf_page_addr + (reloc->cmdbuf.offset & ~PAGE_MASK ); + +if (IS_ENABLED (CONFIG_TEGRA_HOST1X_FIREWALL )) + target += (reloc->cmdbuf.offset &PAGE_MASK ) >>2 ; + *target = reloc_addr; } -if (cmdbuf_page_addr) +if (!IS_ENABLED (CONFIG_TEGRA_HOST1X_FIREWALL ) && cmdbuf_page_addr)host1x_bo_kunmap (cmdbuf, last_page, cmdbuf_page_addr);return 0 ;
@@ -573,6 +586,14 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev)if (err) goto out; +if (IS_ENABLED (CONFIG_TEGRA_HOST1X_FIREWALL ) && !err) { + err =copy_gathers (job, dev); +if (err) { +host1x_job_unpin (job); +return err; + } + } +/* patch gathers */ for (i =0 ; i < job->num_gathers; i++) {struct host1x_job_gather *g = &job->gathers[i];
@@ -581,7 +602,8 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev)if (g->handled)continue ; - g->base = job->gather_addr_phys[i]; +if (!IS_ENABLED (CONFIG_TEGRA_HOST1X_FIREWALL )) + g->base = job->gather_addr_phys[i];for (j = i +1 ; j < job->num_gathers; j++) {if (job->gathers[j].bo == g->bo) {
@@ -590,23 +612,15 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev) } } - err =do_relocs (job, g->bo); + err =do_relocs (job, g);if (err)break ; - err =do_waitchks (job, host, g->bo); + err =do_waitchks (job, host, g);if (err)break ; } -if (IS_ENABLED (CONFIG_TEGRA_HOST1X_FIREWALL ) && !err) { - err =copy_gathers (job, dev); -if (err) { -host1x_job_unpin (job); -return err; - } - } - out:wmb ();
Signature · 1 line
2.13.0
A message that is not in the archive (sent off-list or not yet received): 20170514204734.22130-1-digetx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
PATCH 1/1 drm/tegra: Check whether page belongs to BO in tegra_bo_kmap()
This fixes an OOPS in case of out-of-bounds accessing of a kmap'ed commands buffer CMA while patching relocations in do_relocs().
--- drivers/gpu/drm/tegra/gem.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/tegra/gem.c +3 -0
@@ -74,6 +74,9 @@ static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page) {struct tegra_bo *obj =host1x_to_tegra_bo (bo); +if (page *PAGE_SIZE > obj->gem.size) +return NULL ; +if (obj->vaddr)return obj->vaddr + page *PAGE_SIZE ;else if (obj->gem.import_attach)
Signature · 1 line
2.13.0
A message that is not in the archive (sent off-list or not yet received): 20170514204734.22130-2-digetx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Re: [PATCH] drm/tegra: Check whether page belongs to BO in tegra_bo_kmap()
Quoted 14 lines · Dmitry Osipenko
This fixes an OOPS in case of out-of-bounds accessing of a kmap'ed commands buffer CMA while patching relocations in
do_relocs().Signed-off-by Dmitry Osipenko <[email protected]>--- drivers/gpu/drm/tegra/gem.c | 3 +++ 1 file changed, 3 insertions(+)drivers/gpu/drm/tegra/gem.c +0
-0
drivers/gpu/drm/tegra/gem.c static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page) · line 82
Quoted 9 lines
drivers/gpu/drm/tegra/gem.c +3
-0@@ -74,6 +74,9 @@ static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page) {struct tegra_bo *obj =host1x_to_tegra_bo (bo); +if (page *PAGE_SIZE > obj->gem.size) +return NULL ; +if (obj->vaddr)return obj->vaddr + page *PAGE_SIZE ;else if (obj->gem.import_attach)
It should be '>=', I'll wait for the review comments before sending out a new version of the patch.
Signature · 1 line
Dmitry
PATCH 1/1 drm/tegra: Check offsets of a submitted command buffer and of relocations
In reply to a message not in the archiveIf commands buffer claims a number of words that is higher than its BO can fit, a kernel OOPS will be fired on the out-of-bounds BO access. This was triggered by an opentegra Xorg driver that erroneously pushed too many commands to the pushbuf. The CMDA commands buffer address is 4 bytes aligned, so check the alignment as well.
Add a sanity check for the relocations in a same way.
[ 46.829393] Unable to handle kernel paging request at virtual address f09b2000
...
[<c04a3ba4>] (host1x_job_pin) from [<c04dfcd0>] (tegra_drm_submit+0x474/0x510) [<c04dfcd0>] (tegra_drm_submit) from [<c04deea0>] (tegra_submit+0x50/0x6c) [<c04deea0>] (tegra_submit) from [<c04c07c0>] (drm_ioctl+0x1e4/0x3ec) [<c04c07c0>] (drm_ioctl) from [<c02541a0>] (do_vfs_ioctl+0x9c/0x8e4) [<c02541a0>] (do_vfs_ioctl) from [<c0254a1c>] (SyS_ioctl+0x34/0x5c) [<c0254a1c>] (SyS_ioctl) from [<c0107640>] (ret_fast_syscall+0x0/0x3c)
--- drivers/gpu/drm/tegra/drm.c | 30 ++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/gem.c | 5 ----- drivers/gpu/drm/tegra/gem.h | 5 +++++ 3 files changed, 35 insertions(+), 5 deletions(-)
drivers/gpu/drm/tegra/drm.c +30 -0
@@ -362,6 +362,8 @@ int tegra_drm_submit(struct tegra_drm_context *context,while (num_cmdbufs) {struct drm_tegra_cmdbuf cmdbuf;struct host1x_bo *bo; +struct tegra_bo *obj; +u64 offset;if (copy_from_user (&cmdbuf, cmdbufs,sizeof (cmdbuf))) { err = -EFAULT ;
@@ -374,6 +376,14 @@ int tegra_drm_submit(struct tegra_drm_context *context, goto fail; } + offset = (u64 )cmdbuf.offset + (u64 )cmdbuf.words *sizeof (u32); + obj =host1x_to_tegra_bo (bo); + +if (offset &3 || offset > obj->gem.size) { + err = -EINVAL ; + goto fail; + } +host1x_job_add_gather (job, bo, cmdbuf.words, cmdbuf.offset); num_cmdbufs--; cmdbufs++;
@@ -381,11 +391,31 @@ int tegra_drm_submit(struct tegra_drm_context *context,/* copy and resolve relocations from submit */ while (num_relocs--) { +struct host1x_reloc *reloc; +struct tegra_bo *obj; + err =host1x_reloc_copy_from_user (&job->relocarray[num_relocs], &relocs[num_relocs], drm, file);if (err <0 ) goto fail; + + reloc = &job->relocarray[num_relocs]; + obj =host1x_to_tegra_bo (reloc->cmdbuf.bo); + +if (reloc->cmdbuf.offset &3 || + reloc->cmdbuf.offset > obj->gem.size) { + err = -EINVAL ; + goto fail; + } + + obj =host1x_to_tegra_bo (reloc->target.bo); + +if (reloc->target.offset &3 || + reloc->target.offset > obj->gem.size) { + err = -EINVAL ; + goto fail; + } } if (copy_from_user(job->waitchk, waitchks,
drivers/gpu/drm/tegra/gem.c +0 -5
@@ -20,11 +20,6 @@#include "drm.h" #include "gem.h" -static inline struct tegra_bo *host1x_to_tegra_bo (struct host1x_bo *bo) -{ - return container_of(bo, struct tegra_bo, base); -} -static void tegra_bo_put (struct host1x_bo *bo) {struct tegra_bo *obj =host1x_to_tegra_bo (bo);
drivers/gpu/drm/tegra/gem.h +5 -0
@@ -52,6 +52,11 @@ static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem) return container_of(gem, struct tegra_bo, gem); } +static inline struct tegra_bo *host1x_to_tegra_bo (struct host1x_bo *bo) +{ + return container_of(bo, struct tegra_bo, base); +} +struct tegra_bo *tegra_bo_create (struct drm_device *drm,size_t size,unsigned long flags); struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
Signature · 1 line
2.13.0
A message that is not in the archive (sent off-list or not yet received): 20170514204734.22130-3-digetx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Re: [PATCH] drm/tegra: Check offsets of a submitted command buffer and of relocations
Quoted 18 lines · Dmitry Osipenko
drivers/gpu/drm/tegra/drm.c +11
-0/* copy and resolve relocations from submit */ while (num_relocs--) { +struct host1x_reloc *reloc; +struct tegra_bo *obj; + err =host1x_reloc_copy_from_user (&job->relocarray[num_relocs], &relocs[num_relocs], drm, file);if (err <0 ) goto fail; + + reloc = &job->relocarray[num_relocs]; + obj =host1x_to_tegra_bo (reloc->cmdbuf.bo); + +if (reloc->cmdbuf.offset &3 || + reloc->cmdbuf.offset > obj->gem.size) { + err = -EINVAL ; + goto fail;
drivers/gpu/drm/tegra/drm.c int tegra_drm_submit(struct tegra_drm_context *context, · line 419
Quoted 10 lines
drivers/gpu/drm/tegra/drm.c +9
-0+ } + + obj =host1x_to_tegra_bo (reloc->target.bo); + +if (reloc->target.offset &3 || + reloc->target.offset > obj->gem.size) { + err = -EINVAL ; + goto fail; + } }
The relocation offsets should be compared as '>=' as well.
Signature · 1 line
Dmitry
Re: [PATCH] drm/tegra: Check offsets of a submitted command buffer and of relocations
In reply to a message not in the archiveQuoted 61 lines · Dmitry Osipenko
If commands buffer claims a number of words that is higher than its BO can fit, a kernel OOPS will be fired on the out-of-bounds BO access. This was triggered by an opentegra Xorg driver that erroneously pushed too many commands to the pushbuf. The CMDA commands buffer address is 4 bytes aligned, so check the alignment as well.
Add a sanity check for the relocations in a same way.
[ 46.829393] Unable to handle kernel paging request at virtual address f09b2000...
[<c04a3ba4>] (host1x_job_pin) from [<c04dfcd0>] (tegra_drm_submit+0x474/0x510) [<c04dfcd0>] (tegra_drm_submit) from [<c04deea0>] (tegra_submit+0x50/0x6c) [<c04deea0>] (tegra_submit) from [<c04c07c0>] (drm_ioctl+0x1e4/0x3ec) [<c04c07c0>] (drm_ioctl) from [<c02541a0>] (do_vfs_ioctl+0x9c/0x8e4) [<c02541a0>] (do_vfs_ioctl) from [<c0254a1c>] (SyS_ioctl+0x34/0x5c) [<c0254a1c>] (SyS_ioctl) from [<c0107640>] (ret_fast_syscall+0x0/0x3c)Signed-off-by Dmitry Osipenko <[email protected]>Reviewed-by Erik Faye-Lund <[email protected]>--- drivers/gpu/drm/tegra/drm.c | 30 ++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/gem.c | 5 ----- drivers/gpu/drm/tegra/gem.h | 5 +++++ 3 files changed, 35 insertions(+), 5 deletions(-)drivers/gpu/drm/tegra/drm.c +13
-0@@ -362,6 +362,8 @@ int tegra_drm_submit(struct tegra_drm_context *context,while (num_cmdbufs) {struct drm_tegra_cmdbuf cmdbuf;struct host1x_bo *bo; +struct tegra_bo *obj; +u64 offset;if (copy_from_user (&cmdbuf, cmdbufs,sizeof (cmdbuf))) { err = -EFAULT ;@@ -374,6 +376,14 @@ int tegra_drm_submit(struct tegra_drm_context *context, goto fail; } + offset = (u64 )cmdbuf.offset + (u64 )cmdbuf.words *sizeof (u32); + obj =host1x_to_tegra_bo (bo); + +if (offset &3 || offset > obj->gem.size) { + err = -EINVAL ; + goto fail; + } +host1x_job_add_gather (job, bo, cmdbuf.words, cmdbuf.offset); num_cmdbufs--; cmdbufs++;@@ -381,11 +391,31 @@ int tegra_drm_submit(struct tegra_drm_context *context,/* copy and resolve relocations from submit */ while (num_relocs--) { +struct host1x_reloc *reloc; +struct tegra_bo *obj; + err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs],
drivers/gpu/drm/tegra/drm.c int tegra_drm_submit(struct tegra_drm_context *context, · line 407
Quoted 10 lines
drivers/gpu/drm/tegra/drm.c +6
-0&relocs[num_relocs], drm, file);if (err <0 ) goto fail; + + reloc = &job->relocarray[num_relocs]; + obj =host1x_to_tegra_bo (reloc->cmdbuf.bo); + +if (reloc->cmdbuf.offset &3 || + reloc->cmdbuf.offset > obj->gem.size) {
This could still fail if the bo's size is not divisible by 4, even with >= comparison (we would overwrite the buffer by 1 to 3 bytes). I would do the same as in the gather case, i.e. find out the address immediately after the write and compare using >. Perhaps add a helper function if it makes sense. I also don't think the "& 3" checks are needed.
Quoted 47 lines
drivers/gpu/drm/tegra/drm.c +11
-0+ err = -EINVAL ; + goto fail; + } + + obj =host1x_to_tegra_bo (reloc->target.bo); + +if (reloc->target.offset &3 || + reloc->target.offset > obj->gem.size) { + err = -EINVAL ; + goto fail; + } } if (copy_from_user(job->waitchk, waitchks,drivers/gpu/drm/tegra/gem.c +0
-5@@ -20,11 +20,6 @@#include "drm.h" #include "gem.h" -static inline struct tegra_bo *host1x_to_tegra_bo (struct host1x_bo *bo) -{ - return container_of(bo, struct tegra_bo, base); -} -static void tegra_bo_put (struct host1x_bo *bo) {struct tegra_bo *obj =host1x_to_tegra_bo (bo);drivers/gpu/drm/tegra/gem.h +5
-0@@ -52,6 +52,11 @@ static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem) return container_of(gem, struct tegra_bo, gem); } +static inline struct tegra_bo *host1x_to_tegra_bo (struct host1x_bo *bo) +{ + return container_of(bo, struct tegra_bo, base); +} +struct tegra_bo *tegra_bo_create (struct drm_device *drm,size_t size,unsigned long flags); struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
A message that is not in the archive (sent off-list or not yet received): 3db57134-4dba-e886-059c-96034192f637-/[email protected]
Re: [PATCH] drm/tegra: Check offsets of a submitted command buffer and of relocations
Quoted 81 lines · Mikko Perttunen
Quoted 80 lines · Dmitry Osipenko
If commands buffer claims a number of words that is higher than its BO can fit, a kernel OOPS will be fired on the out-of-bounds BO access. This was triggered by an opentegra Xorg driver that erroneously pushed too many commands to the pushbuf. The CMDA commands buffer address is 4 bytes aligned, so check the alignment as well.
Add a sanity check for the relocations in a same way.
[ 46.829393] Unable to handle kernel paging request at virtual address
f09b2000
...
[<c04a3ba4>] (host1x_job_pin) from [<c04dfcd0>] (tegra_drm_submit+0x474/0x510)
[<c04dfcd0>] (tegra_drm_submit) from [<c04deea0>] (tegra_submit+0x50/0x6c) [<c04deea0>] (tegra_submit) from [<c04c07c0>] (drm_ioctl+0x1e4/0x3ec) [<c04c07c0>] (drm_ioctl) from [<c02541a0>] (do_vfs_ioctl+0x9c/0x8e4) [<c02541a0>] (do_vfs_ioctl) from [<c0254a1c>] (SyS_ioctl+0x34/0x5c) [<c0254a1c>] (SyS_ioctl) from [<c0107640>] (ret_fast_syscall+0x0/0x3c)Signed-off-by Dmitry Osipenko <[email protected]>Reviewed-by Erik Faye-Lund <[email protected]>--- drivers/gpu/drm/tegra/drm.c | 30 ++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/gem.c | 5 ----- drivers/gpu/drm/tegra/gem.h | 5 +++++ 3 files changed, 35 insertions(+), 5 deletions(-)drivers/gpu/drm/tegra/drm.c +2
-0@@ -362,6 +362,8 @@ int tegra_drm_submit(struct tegra_drm_context *context, while (num_cmdbufs) {struct drm_tegra_cmdbuf cmdbuf;struct host1x_bo *bo; +struct tegra_bo *obj; +u64 offset;if (copy_from_user (&cmdbuf, cmdbufs,sizeof (cmdbuf))) {err = -EFAULT;drivers/gpu/drm/tegra/drm.c +8
-0@@ -374,6 +376,14 @@ int tegra_drm_submit(struct tegra_drm_context *context, goto fail; } + offset = (u64 )cmdbuf.offset + (u64 )cmdbuf.words *sizeof (u32); + obj =host1x_to_tegra_bo (bo); + +if (offset &3 || offset > obj->gem.size) { + err = -EINVAL ; + goto fail; + } + host1x_job_add_gather(job, bo, cmdbuf.words,cmdbuf.offset); num_cmdbufs--; cmdbufs++;drivers/gpu/drm/tegra/drm.c +8
-0@@ -381,11 +391,31 @@ int tegra_drm_submit(struct tegra_drm_context *context,/* copy and resolve relocations from submit */ while (num_relocs--) { +struct host1x_reloc *reloc; +struct tegra_bo *obj; + err =host1x_reloc_copy_from_user (&job->relocarray[num_relocs], &relocs[num_relocs], drm, file);if (err <0 ) goto fail; + + reloc = &job->relocarray[num_relocs]; + obj =host1x_to_tegra_bo (reloc->cmdbuf.bo); + + if (reloc->cmdbuf.offset & 3 ||
Quoted 8 lines
drivers/gpu/drm/tegra/drm.c int tegra_drm_submit(struct tegra_drm_context *context, · line 407
Quoted 1 line
drivers/gpu/drm/tegra/drm.c +1
-0+ reloc->cmdbuf.offset > obj->gem.size) {This could still fail if the bo's size is not divisible by 4, even with >= comparison (we would overwrite the buffer by 1 to 3 bytes). I would do the same as in the gather case, i.e. find out the address immediately after the write and compare using >. Perhaps add a helper function if it makes sense. I also don't think the "& 3" checks are needed.
The bo-size is always a multiple of PAGE_SIZE, due to the rounding in tegra_bo_alloc_object(), so I don't think this actually can fail. But maybe we want to future-proof this code for a potential future where this is not the case?
A message that is not in the archive (sent off-list or not yet received): CABPQNSYgayogP1MCTj5O4UNFrsWr5e5F9e8E0wHMU0bieW+Ocw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org
Re: [PATCH] drm/tegra: Check offsets of a submitted command buffer and of relocations
Quoted 91 lines · Erik Faye-Lund
Quoted 81 lines · Mikko Perttunen
Quoted 80 lines · Dmitry Osipenko
If commands buffer claims a number of words that is higher than its BO can fit, a kernel OOPS will be fired on the out-of-bounds BO access. This was triggered by an opentegra Xorg driver that erroneously pushed too many commands to the pushbuf. The CMDA commands buffer address is 4 bytes aligned, so check the alignment as well.
Add a sanity check for the relocations in a same way.
[ 46.829393] Unable to handle kernel paging request at virtual address
f09b2000
...
[<c04a3ba4>] (host1x_job_pin) from [<c04dfcd0>] (tegra_drm_submit+0x474/0x510)
[<c04dfcd0>] (tegra_drm_submit) from [<c04deea0>] (tegra_submit+0x50/0x6c) [<c04deea0>] (tegra_submit) from [<c04c07c0>] (drm_ioctl+0x1e4/0x3ec) [<c04c07c0>] (drm_ioctl) from [<c02541a0>] (do_vfs_ioctl+0x9c/0x8e4) [<c02541a0>] (do_vfs_ioctl) from [<c0254a1c>] (SyS_ioctl+0x34/0x5c) [<c0254a1c>] (SyS_ioctl) from [<c0107640>] (ret_fast_syscall+0x0/0x3c)Signed-off-by Dmitry Osipenko <[email protected]>Reviewed-by Erik Faye-Lund <[email protected]>--- drivers/gpu/drm/tegra/drm.c | 30 ++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/gem.c | 5 ----- drivers/gpu/drm/tegra/gem.h | 5 +++++ 3 files changed, 35 insertions(+), 5 deletions(-)drivers/gpu/drm/tegra/drm.c +2
-0@@ -362,6 +362,8 @@ int tegra_drm_submit(struct tegra_drm_context *context, while (num_cmdbufs) {struct drm_tegra_cmdbuf cmdbuf;struct host1x_bo *bo; +struct tegra_bo *obj; +u64 offset;if (copy_from_user (&cmdbuf, cmdbufs,sizeof (cmdbuf))) {err = -EFAULT;drivers/gpu/drm/tegra/drm.c +8
-0@@ -374,6 +376,14 @@ int tegra_drm_submit(struct tegra_drm_context *context, goto fail; } + offset = (u64 )cmdbuf.offset + (u64 )cmdbuf.words *sizeof (u32); + obj =host1x_to_tegra_bo (bo); + +if (offset &3 || offset > obj->gem.size) { + err = -EINVAL ; + goto fail; + } + host1x_job_add_gather(job, bo, cmdbuf.words,cmdbuf.offset); num_cmdbufs--; cmdbufs++;drivers/gpu/drm/tegra/drm.c +8
-0@@ -381,11 +391,31 @@ int tegra_drm_submit(struct tegra_drm_context *context,/* copy and resolve relocations from submit */ while (num_relocs--) { +struct host1x_reloc *reloc; +struct tegra_bo *obj; + err =host1x_reloc_copy_from_user (&job->relocarray[num_relocs], &relocs[num_relocs], drm, file);if (err <0 ) goto fail; + + reloc = &job->relocarray[num_relocs]; + obj =host1x_to_tegra_bo (reloc->cmdbuf.bo); + + if (reloc->cmdbuf.offset & 3 ||Quoted 8 lines
drivers/gpu/drm/tegra/drm.c int tegra_drm_submit(struct tegra_drm_context *context, · line 407
Quoted 1 line
drivers/gpu/drm/tegra/drm.c +1
-0+ reloc->cmdbuf.offset > obj->gem.size) {This could still fail if the bo's size is not divisible by 4, even with >= comparison (we would overwrite the buffer by 1 to 3 bytes). I would do the same as in the gather case, i.e. find out the address immediately after the write and compare using >. Perhaps add a helper function if it makes sense. I also don't think the "& 3" checks are needed.
Quoted 5 lines
The bo-size is always a multiple of
PAGE_SIZE, due to the rounding integra_bo_alloc_object(), so I don't think this actually can fail. But maybe we want to future-proof this code for a potential future where this is not the case?
Yeah, I think whether or not this might change in the future, the code would be easier to understand with the change.
A message that is not in the archive (sent off-list or not yet received): 2fa29660-8982-7caa-065c-b04e2fd2a8b6-/[email protected]
Re: [PATCH] drm/tegra: Check offsets of a submitted command buffer and of relocations
Quoted 94 lines · Mikko Perttunen
Quoted 91 lines · Erik Faye-Lund
Quoted 89 lines · Mikko Perttunen
Quoted 71 lines · Dmitry Osipenko
If commands buffer claims a number of words that is higher than its BO can fit, a kernel OOPS will be fired on the out-of-bounds BO access. This was triggered by an opentegra Xorg driver that erroneously pushed too many commands to the pushbuf. The CMDA commands buffer address is 4 bytes aligned, so check the alignment as well.
Add a sanity check for the relocations in a same way.
[ 46.829393] Unable to handle kernel paging request at virtual address
f09b2000
...
[<c04a3ba4>] (host1x_job_pin) from [<c04dfcd0>] (tegra_drm_submit+0x474/0x510)
[<c04dfcd0>] (tegra_drm_submit) from [<c04deea0>] (tegra_submit+0x50/0x6c) [<c04deea0>] (tegra_submit) from [<c04c07c0>] (drm_ioctl+0x1e4/0x3ec) [<c04c07c0>] (drm_ioctl) from [<c02541a0>] (do_vfs_ioctl+0x9c/0x8e4) [<c02541a0>] (do_vfs_ioctl) from [<c0254a1c>] (SyS_ioctl+0x34/0x5c) [<c0254a1c>] (SyS_ioctl) from [<c0107640>] (ret_fast_syscall+0x0/0x3c)Signed-off-by Dmitry Osipenko <[email protected]>Reviewed-by Erik Faye-Lund <[email protected]>--- drivers/gpu/drm/tegra/drm.c | 30 ++++++++++++++++++++++++++++++ drivers/gpu/drm/tegra/gem.c | 5 ----- drivers/gpu/drm/tegra/gem.h | 5 +++++ 3 files changed, 35 insertions(+), 5 deletions(-)drivers/gpu/drm/tegra/drm.c +2
-0@@ -362,6 +362,8 @@ int tegra_drm_submit(struct tegra_drm_context *context, while (num_cmdbufs) {struct drm_tegra_cmdbuf cmdbuf;struct host1x_bo *bo; +struct tegra_bo *obj; +u64 offset;if (copy_from_user (&cmdbuf, cmdbufs,sizeof (cmdbuf))) {err = -EFAULT;drivers/gpu/drm/tegra/drm.c +8
-0@@ -374,6 +376,14 @@ int tegra_drm_submit(struct tegra_drm_context *context, goto fail; } + offset = (u64 )cmdbuf.offset + (u64 )cmdbuf.words *sizeof (u32); + obj =host1x_to_tegra_bo (bo); + +if (offset &3 || offset > obj->gem.size) { + err = -EINVAL ; + goto fail; + } + host1x_job_add_gather(job, bo, cmdbuf.words,cmdbuf.offset); num_cmdbufs--; cmdbufs++;drivers/gpu/drm/tegra/drm.c +3
-0@@ -381,11 +391,31 @@ int tegra_drm_submit(struct tegra_drm_context *context,/* copy and resolve relocations from submit */ while (num_relocs--) { +struct host1x_reloc *reloc; +struct tegra_bo *obj; + err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs], &relocs[num_relocs],drivers/gpu/drm/tegra/drm.c int tegra_drm_submit(struct tegra_drm_context · line 410
Quoted 10 lines
drivers/gpu/drm/tegra/drm.c +6
-0drm, file);if (err <0 ) goto fail; + + reloc = &job->relocarray[num_relocs]; + obj =host1x_to_tegra_bo (reloc->cmdbuf.bo); + +if (reloc->cmdbuf.offset &3 || + reloc->cmdbuf.offset > obj->gem.size) {This could still fail if the bo's size is not divisible by 4, even with >= comparison (we would overwrite the buffer by 1 to 3 bytes). I would do the same as in the gather case, i.e. find out the address immediately after the write and compare using >. Perhaps add a helper function if it makes sense. I also don't think the "& 3" checks are needed.
Quoted 8 lines
Quoted 5 lines
The bo-size is always a multiple of
PAGE_SIZE, due to the rounding integra_bo_alloc_object(), so I don't think this actually can fail. But maybe we want to future-proof this code for a potential future where this is not the case?Yeah, I think whether or not this might change in the future, the code would be easier to understand with the change.
It's very unlikely to me that BO size round-upping may go away. I'll add a comment for that alignment check to make it clear in the code.
Signature · 1 line
Dmitry
Re: [PATCH] drm/tegra: Check offsets of a submitted command buffer and of relocations
In reply to a message not in the archiveQuoted 10 lines · Mikko Perttunen
drivers/gpu/drm/tegra/drm.c int tegra_drm_submit(struct tegra_drm_context *context, · line 407
Quoted 2 lines · Dmitry Osipenko
drivers/gpu/drm/tegra/drm.c +2
-0+if (reloc->cmdbuf.offset &3 || + reloc->cmdbuf.offset > obj->gem.size) {This could still fail if the bo's size is not divisible by 4, even with >= comparison (we would overwrite the buffer by 1 to 3 bytes). I would do the same as in the gather case, i.e. find out the address immediately after the write and compare using >. Perhaps add a helper function if it makes sense. I also don't think the "& 3" checks are needed.
Sorry, I forgot to mention the reason of the relocations cmdbuf offset alignment requirement in the commit description. So the reason is that during the patching of the gather in do_relocs(), the unaligned offset would cause a partial overwrite of the command buffer word following the relocation and only a part of the relocation address being written to the actual address word.
PATCH v2 1/1 drm/tegra: Correct idr_alloc() minimum id
In reply to a message not in the archiveThe client ID 0 is reserved by the host1x/cdma to mark the timeout timer work as already been scheduled and context ID is used as the clients one. This fixes spurious CDMA timeouts.
---
v2: Changed the commit description, now explains the cause of CDMA timeouts.
drivers/gpu/drm/tegra/drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/tegra/drm.c +1 -1
@@ -548,7 +548,7 @@ static int tegra_client_open(struct tegra_drm_file *fpriv,if (err <0 )return err; - err =idr_alloc (&fpriv->contexts, context,0 ,0 ,GFP_KERNEL ); + err =idr_alloc (&fpriv->contexts, context,1 ,0 ,GFP_KERNEL );if (err <0 ) { client->ops->close_channel (context);return err;
Signature · 1 line
2.13.0
A message that is not in the archive (sent off-list or not yet received): 20170514204734.22130-4-digetx-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Mikko Perttunen
Reviewed-byRe: [PATCH v2] drm/tegra: Correct idr_alloc() minimum id
Quoted 27 lines · Dmitry Osipenko
The client ID 0 is reserved by the host1x/cdma to mark the timeout timer work as already been scheduled and context ID is used as the clients one. This fixes spurious CDMA timeouts.
Fixes bdd2f9cd10eb ("drm/tegra: Don't leak kernel pointer to userspace")Signed-off-by Dmitry Osipenko <[email protected]>---
v2: Changed the commit description, now explains the cause of CDMA timeouts.
drivers/gpu/drm/tegra/drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/gpu/drm/tegra/drm.c +1
-1@@ -548,7 +548,7 @@ static int tegra_client_open(struct tegra_drm_file *fpriv,if (err <0 )return err; - err =idr_alloc (&fpriv->contexts, context,0 ,0 ,GFP_KERNEL ); + err =idr_alloc (&fpriv->contexts, context,1 ,0 ,GFP_KERNEL );if (err <0 ) { client->ops->close_channel (context);return err;