Commit 961e3bea authored by Thierry Reding's avatar Thierry Reding

drm/tegra: Make job submission 64-bit safe

Job submission currently relies on the fact that struct drm_tegra_reloc
and struct host1x_reloc are the same size and uses a simple call to the
copy_from_user() function to copy them to kernel space. This causes the
handle to be stored in the buffer object field, which then needs a cast
to a 32 bit integer to resolve it to a proper buffer object pointer and
store it back in the buffer object field.

On 64-bit architectures that will no longer work, since pointers are 64
bits wide whereas handles will remain 32 bits. This causes the sizes of
both structures to because different and copying will no longer work.

Fix this by adding a new function, host1x_reloc_get_user(), that copies
the structures field by field.

While at it, use substructures for the command and target buffers in
struct host1x_reloc for better readability. Also use unsized types to
make it more obvious that this isn't part of userspace ABI.
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent bd4f2360
...@@ -132,6 +132,45 @@ host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle) ...@@ -132,6 +132,45 @@ host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle)
return &bo->base; return &bo->base;
} }
static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
struct drm_tegra_reloc __user *src,
struct drm_device *drm,
struct drm_file *file)
{
u32 cmdbuf, target;
int err;
err = get_user(cmdbuf, &src->cmdbuf.handle);
if (err < 0)
return err;
err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset);
if (err < 0)
return err;
err = get_user(target, &src->target.handle);
if (err < 0)
return err;
err = get_user(dest->target.offset, &src->cmdbuf.offset);
if (err < 0)
return err;
err = get_user(dest->shift, &src->shift);
if (err < 0)
return err;
dest->cmdbuf.bo = host1x_bo_lookup(drm, file, cmdbuf);
if (!dest->cmdbuf.bo)
return -ENOENT;
dest->target.bo = host1x_bo_lookup(drm, file, target);
if (!dest->target.bo)
return -ENOENT;
return 0;
}
int tegra_drm_submit(struct tegra_drm_context *context, int tegra_drm_submit(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm, struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file) struct drm_file *file)
...@@ -184,27 +223,14 @@ int tegra_drm_submit(struct tegra_drm_context *context, ...@@ -184,27 +223,14 @@ int tegra_drm_submit(struct tegra_drm_context *context,
cmdbufs++; cmdbufs++;
} }
if (copy_from_user(job->relocarray, relocs, /* copy and resolve relocations from submit */
sizeof(*relocs) * num_relocs)) {
err = -EFAULT;
goto fail;
}
while (num_relocs--) { while (num_relocs--) {
struct host1x_reloc *reloc = &job->relocarray[num_relocs]; err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs],
struct host1x_bo *cmdbuf, *target; &relocs[num_relocs], drm,
file);
cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf); if (err < 0)
target = host1x_bo_lookup(drm, file, (u32)reloc->target);
reloc->cmdbuf = cmdbuf;
reloc->target = target;
if (!reloc->target || !reloc->cmdbuf) {
err = -ENOENT;
goto fail; goto fail;
} }
}
if (copy_from_user(job->waitchk, waitchks, if (copy_from_user(job->waitchk, waitchks,
sizeof(*waitchks) * num_waitchks)) { sizeof(*waitchks) * num_waitchks)) {
......
...@@ -185,16 +185,16 @@ static unsigned int pin_job(struct host1x_job *job) ...@@ -185,16 +185,16 @@ static unsigned int pin_job(struct host1x_job *job)
struct sg_table *sgt; struct sg_table *sgt;
dma_addr_t phys_addr; dma_addr_t phys_addr;
reloc->target = host1x_bo_get(reloc->target); reloc->target.bo = host1x_bo_get(reloc->target.bo);
if (!reloc->target) if (!reloc->target.bo)
goto unpin; goto unpin;
phys_addr = host1x_bo_pin(reloc->target, &sgt); phys_addr = host1x_bo_pin(reloc->target.bo, &sgt);
if (!phys_addr) if (!phys_addr)
goto unpin; goto unpin;
job->addr_phys[job->num_unpins] = phys_addr; job->addr_phys[job->num_unpins] = phys_addr;
job->unpins[job->num_unpins].bo = reloc->target; job->unpins[job->num_unpins].bo = reloc->target.bo;
job->unpins[job->num_unpins].sgt = sgt; job->unpins[job->num_unpins].sgt = sgt;
job->num_unpins++; job->num_unpins++;
} }
...@@ -235,21 +235,21 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) ...@@ -235,21 +235,21 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
for (i = 0; i < job->num_relocs; i++) { for (i = 0; i < job->num_relocs; i++) {
struct host1x_reloc *reloc = &job->relocarray[i]; struct host1x_reloc *reloc = &job->relocarray[i];
u32 reloc_addr = (job->reloc_addr_phys[i] + u32 reloc_addr = (job->reloc_addr_phys[i] +
reloc->target_offset) >> reloc->shift; reloc->target.offset) >> reloc->shift;
u32 *target; u32 *target;
/* skip all other gathers */ /* skip all other gathers */
if (cmdbuf != reloc->cmdbuf) if (cmdbuf != reloc->cmdbuf.bo)
continue; continue;
if (last_page != reloc->cmdbuf_offset >> PAGE_SHIFT) { if (last_page != reloc->cmdbuf.offset >> PAGE_SHIFT) {
if (cmdbuf_page_addr) if (cmdbuf_page_addr)
host1x_bo_kunmap(cmdbuf, last_page, host1x_bo_kunmap(cmdbuf, last_page,
cmdbuf_page_addr); cmdbuf_page_addr);
cmdbuf_page_addr = host1x_bo_kmap(cmdbuf, cmdbuf_page_addr = host1x_bo_kmap(cmdbuf,
reloc->cmdbuf_offset >> PAGE_SHIFT); reloc->cmdbuf.offset >> PAGE_SHIFT);
last_page = reloc->cmdbuf_offset >> PAGE_SHIFT; last_page = reloc->cmdbuf.offset >> PAGE_SHIFT;
if (unlikely(!cmdbuf_page_addr)) { if (unlikely(!cmdbuf_page_addr)) {
pr_err("Could not map cmdbuf for relocation\n"); pr_err("Could not map cmdbuf for relocation\n");
...@@ -257,7 +257,7 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) ...@@ -257,7 +257,7 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
} }
} }
target = cmdbuf_page_addr + (reloc->cmdbuf_offset & ~PAGE_MASK); target = cmdbuf_page_addr + (reloc->cmdbuf.offset & ~PAGE_MASK);
*target = reloc_addr; *target = reloc_addr;
} }
...@@ -272,7 +272,7 @@ static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf, ...@@ -272,7 +272,7 @@ static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
{ {
offset *= sizeof(u32); offset *= sizeof(u32);
if (reloc->cmdbuf != cmdbuf || reloc->cmdbuf_offset != offset) if (reloc->cmdbuf.bo != cmdbuf || reloc->cmdbuf.offset != offset)
return false; return false;
return true; return true;
......
...@@ -164,12 +164,15 @@ int host1x_job_submit(struct host1x_job *job); ...@@ -164,12 +164,15 @@ int host1x_job_submit(struct host1x_job *job);
*/ */
struct host1x_reloc { struct host1x_reloc {
struct host1x_bo *cmdbuf; struct {
u32 cmdbuf_offset; struct host1x_bo *bo;
struct host1x_bo *target; unsigned long offset;
u32 target_offset; } cmdbuf;
u32 shift; struct {
u32 pad; struct host1x_bo *bo;
unsigned long offset;
} target;
unsigned long shift;
}; };
struct host1x_job { struct host1x_job {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment