Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpu: host1x: Copy gathers before verification

The firewall verified gather buffers before copying them. This
allowed a malicious application to rewrite the buffer content by
timing the rewrite carefully.

This patch makes the buffer validation occur after copying the
buffers.

Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>

authored by

Arto Merilainen and committed by
Thierry Reding
3364cd28 afac0e43

+20 -31
+20 -31
drivers/gpu/host1x/job.c
··· 228 228 void *cmdbuf_page_addr = NULL; 229 229 230 230 /* pin & patch the relocs for one gather */ 231 - while (i < job->num_relocs) { 231 + for (i = 0; i < job->num_relocs; i++) { 232 232 struct host1x_reloc *reloc = &job->relocarray[i]; 233 233 u32 reloc_addr = (job->reloc_addr_phys[i] + 234 234 reloc->target_offset) >> reloc->shift; 235 235 u32 *target; 236 236 237 237 /* skip all other gathers */ 238 - if (!(reloc->cmdbuf && cmdbuf == reloc->cmdbuf)) { 239 - i++; 238 + if (cmdbuf != reloc->cmdbuf) 240 239 continue; 241 - } 242 240 243 241 if (last_page != reloc->cmdbuf_offset >> PAGE_SHIFT) { 244 242 if (cmdbuf_page_addr) ··· 255 257 256 258 target = cmdbuf_page_addr + (reloc->cmdbuf_offset & ~PAGE_MASK); 257 259 *target = reloc_addr; 258 - 259 - /* mark this gather as handled */ 260 - reloc->cmdbuf = 0; 261 260 } 262 261 263 262 if (cmdbuf_page_addr) ··· 373 378 374 379 static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g) 375 380 { 376 - u32 *cmdbuf_base; 381 + u32 *cmdbuf_base = (u32 *)fw->job->gather_copy_mapped + 382 + (g->offset / sizeof(u32)); 377 383 int err = 0; 378 384 379 385 if (!fw->job->is_addr_reg) 380 386 return 0; 381 387 382 - cmdbuf_base = host1x_bo_mmap(g->bo); 383 - if (!cmdbuf_base) 384 - return -ENOMEM; 385 388 fw->words = g->words; 386 389 fw->cmdbuf_id = g->bo; 387 390 fw->offset = 0; ··· 446 453 447 454 static inline int copy_gathers(struct host1x_job *job, struct device *dev) 448 455 { 456 + struct host1x_firewall fw; 449 457 size_t size = 0; 450 458 size_t offset = 0; 451 459 int i; 460 + 461 + fw.job = job; 462 + fw.dev = dev; 463 + fw.reloc = job->relocarray; 464 + fw.num_relocs = job->num_relocs; 465 + fw.class = 0; 452 466 453 467 for (i = 0; i < job->num_gathers; i++) { 454 468 struct host1x_job_gather *g = &job->gathers[i]; ··· 477 477 struct host1x_job_gather *g = &job->gathers[i]; 478 478 void *gather; 479 479 480 + /* Copy the gather */ 480 481 gather = host1x_bo_mmap(g->bo); 481 482 memcpy(job->gather_copy_mapped + offset, gather + g->offset, 482 483 g->words * sizeof(u32)); 483 484 host1x_bo_munmap(g->bo, gather); 484 485 486 + /* Store the location in the buffer */ 485 487 g->base = job->gather_copy; 486 488 g->offset = offset; 487 - g->bo = NULL; 489 + 490 + /* Validate the job */ 491 + if (validate(&fw, g)) 492 + return -EINVAL; 488 493 489 494 offset += g->words * sizeof(u32); 490 495 } ··· 502 497 int err; 503 498 unsigned int i, j; 504 499 struct host1x *host = dev_get_drvdata(dev->parent); 505 - struct host1x_firewall fw; 506 500 DECLARE_BITMAP(waitchk_mask, host1x_syncpt_nb_pts(host)); 507 - 508 - fw.job = job; 509 - fw.dev = dev; 510 - fw.reloc = job->relocarray; 511 - fw.num_relocs = job->num_relocs; 512 - fw.class = 0; 513 501 514 502 bitmap_zero(waitchk_mask, host1x_syncpt_nb_pts(host)); 515 503 for (i = 0; i < job->num_waitchk; i++) { ··· 534 536 if (job->gathers[j].bo == g->bo) 535 537 job->gathers[j].handled = true; 536 538 537 - err = 0; 538 - 539 - if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) 540 - err = validate(&fw, g); 541 - 539 + err = do_relocs(job, g->bo); 542 540 if (err) 543 - dev_err(dev, "Job invalid (err=%d)\n", err); 541 + break; 544 542 545 - if (!err) 546 - err = do_relocs(job, g->bo); 547 - 548 - if (!err) 549 - err = do_waitchks(job, host, g->bo); 550 - 543 + err = do_waitchks(job, host, g->bo); 551 544 if (err) 552 545 break; 553 546 }