aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorChristian König <christian.koenig@amd.com>2015-05-07 09:19:22 -0400
committerAlex Deucher <alexander.deucher@amd.com>2015-05-07 11:00:16 -0400
commit29c63fe22a17c64e54016040cd882481bd45ee5a (patch)
tree7f8495f172b56791c0b06b65225e0e465b91efa0 /drivers/gpu
parent247c405098ab731ad9b58971e2cfbab116b54b45 (diff)
drm/radeon: make VCE handle check more strict
Invalid handles can crash the hw. Signed-off-by: Christian König <christian.koenig@amd.com> CC: stable@vger.kernel.org Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/radeon/radeon_vce.c65
1 files changed, 48 insertions, 17 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c
index 24f849f888bb..0de5711ac508 100644
--- a/drivers/gpu/drm/radeon/radeon_vce.c
+++ b/drivers/gpu/drm/radeon/radeon_vce.c
@@ -493,18 +493,27 @@ int radeon_vce_cs_reloc(struct radeon_cs_parser *p, int lo, int hi,
493 * 493 *
494 * @p: parser context 494 * @p: parser context
495 * @handle: handle to validate 495 * @handle: handle to validate
496 * @allocated: allocated a new handle?
496 * 497 *
497 * Validates the handle and return the found session index or -EINVAL 498 * Validates the handle and return the found session index or -EINVAL
498 * we we don't have another free session index. 499 * we we don't have another free session index.
499 */ 500 */
500int radeon_vce_validate_handle(struct radeon_cs_parser *p, uint32_t handle) 501static int radeon_vce_validate_handle(struct radeon_cs_parser *p,
502 uint32_t handle, bool *allocated)
501{ 503{
502 unsigned i; 504 unsigned i;
503 505
506 *allocated = false;
507
504 /* validate the handle */ 508 /* validate the handle */
505 for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) { 509 for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) {
506 if (atomic_read(&p->rdev->vce.handles[i]) == handle) 510 if (atomic_read(&p->rdev->vce.handles[i]) == handle) {
511 if (p->rdev->vce.filp[i] != p->filp) {
512 DRM_ERROR("VCE handle collision detected!\n");
513 return -EINVAL;
514 }
507 return i; 515 return i;
516 }
508 } 517 }
509 518
510 /* handle not found try to alloc a new one */ 519 /* handle not found try to alloc a new one */
@@ -512,6 +521,7 @@ int radeon_vce_validate_handle(struct radeon_cs_parser *p, uint32_t handle)
512 if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) { 521 if (!atomic_cmpxchg(&p->rdev->vce.handles[i], 0, handle)) {
513 p->rdev->vce.filp[i] = p->filp; 522 p->rdev->vce.filp[i] = p->filp;
514 p->rdev->vce.img_size[i] = 0; 523 p->rdev->vce.img_size[i] = 0;
524 *allocated = true;
515 return i; 525 return i;
516 } 526 }
517 } 527 }
@@ -529,10 +539,10 @@ int radeon_vce_validate_handle(struct radeon_cs_parser *p, uint32_t handle)
529int radeon_vce_cs_parse(struct radeon_cs_parser *p) 539int radeon_vce_cs_parse(struct radeon_cs_parser *p)
530{ 540{
531 int session_idx = -1; 541 int session_idx = -1;
532 bool destroyed = false; 542 bool destroyed = false, created = false, allocated = false;
533 uint32_t tmp, handle = 0; 543 uint32_t tmp, handle = 0;
534 uint32_t *size = &tmp; 544 uint32_t *size = &tmp;
535 int i, r; 545 int i, r = 0;
536 546
537 while (p->idx < p->chunk_ib->length_dw) { 547 while (p->idx < p->chunk_ib->length_dw) {
538 uint32_t len = radeon_get_ib_value(p, p->idx); 548 uint32_t len = radeon_get_ib_value(p, p->idx);
@@ -540,18 +550,21 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p)
540 550
541 if ((len < 8) || (len & 3)) { 551 if ((len < 8) || (len & 3)) {
542 DRM_ERROR("invalid VCE command length (%d)!\n", len); 552 DRM_ERROR("invalid VCE command length (%d)!\n", len);
543 return -EINVAL; 553 r = -EINVAL;
554 goto out;
544 } 555 }
545 556
546 if (destroyed) { 557 if (destroyed) {
547 DRM_ERROR("No other command allowed after destroy!\n"); 558 DRM_ERROR("No other command allowed after destroy!\n");
548 return -EINVAL; 559 r = -EINVAL;
560 goto out;
549 } 561 }
550 562
551 switch (cmd) { 563 switch (cmd) {
552 case 0x00000001: // session 564 case 0x00000001: // session
553 handle = radeon_get_ib_value(p, p->idx + 2); 565 handle = radeon_get_ib_value(p, p->idx + 2);
554 session_idx = radeon_vce_validate_handle(p, handle); 566 session_idx = radeon_vce_validate_handle(p, handle,
567 &allocated);
555 if (session_idx < 0) 568 if (session_idx < 0)
556 return session_idx; 569 return session_idx;
557 size = &p->rdev->vce.img_size[session_idx]; 570 size = &p->rdev->vce.img_size[session_idx];
@@ -561,6 +574,13 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p)
561 break; 574 break;
562 575
563 case 0x01000001: // create 576 case 0x01000001: // create
577 created = true;
578 if (!allocated) {
579 DRM_ERROR("Handle already in use!\n");
580 r = -EINVAL;
581 goto out;
582 }
583
564 *size = radeon_get_ib_value(p, p->idx + 8) * 584 *size = radeon_get_ib_value(p, p->idx + 8) *
565 radeon_get_ib_value(p, p->idx + 10) * 585 radeon_get_ib_value(p, p->idx + 10) *
566 8 * 3 / 2; 586 8 * 3 / 2;
@@ -578,12 +598,12 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p)
578 r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9, 598 r = radeon_vce_cs_reloc(p, p->idx + 10, p->idx + 9,
579 *size); 599 *size);
580 if (r) 600 if (r)
581 return r; 601 goto out;
582 602
583 r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11, 603 r = radeon_vce_cs_reloc(p, p->idx + 12, p->idx + 11,
584 *size / 3); 604 *size / 3);
585 if (r) 605 if (r)
586 return r; 606 goto out;
587 break; 607 break;
588 608
589 case 0x02000001: // destroy 609 case 0x02000001: // destroy
@@ -594,7 +614,7 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p)
594 r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, 614 r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2,
595 *size * 2); 615 *size * 2);
596 if (r) 616 if (r)
597 return r; 617 goto out;
598 break; 618 break;
599 619
600 case 0x05000004: // video bitstream buffer 620 case 0x05000004: // video bitstream buffer
@@ -602,36 +622,47 @@ int radeon_vce_cs_parse(struct radeon_cs_parser *p)
602 r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, 622 r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2,
603 tmp); 623 tmp);
604 if (r) 624 if (r)
605 return r; 625 goto out;
606 break; 626 break;
607 627
608 case 0x05000005: // feedback buffer 628 case 0x05000005: // feedback buffer
609 r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2, 629 r = radeon_vce_cs_reloc(p, p->idx + 3, p->idx + 2,
610 4096); 630 4096);
611 if (r) 631 if (r)
612 return r; 632 goto out;
613 break; 633 break;
614 634
615 default: 635 default:
616 DRM_ERROR("invalid VCE command (0x%x)!\n", cmd); 636 DRM_ERROR("invalid VCE command (0x%x)!\n", cmd);
617 return -EINVAL; 637 r = -EINVAL;
638 goto out;
618 } 639 }
619 640
620 if (session_idx == -1) { 641 if (session_idx == -1) {
621 DRM_ERROR("no session command at start of IB\n"); 642 DRM_ERROR("no session command at start of IB\n");
622 return -EINVAL; 643 r = -EINVAL;
644 goto out;
623 } 645 }
624 646
625 p->idx += len / 4; 647 p->idx += len / 4;
626 } 648 }
627 649
628 if (destroyed) { 650 if (allocated && !created) {
629 /* IB contains a destroy msg, free the handle */ 651 DRM_ERROR("New session without create command!\n");
652 r = -ENOENT;
653 }
654
655out:
656 if ((!r && destroyed) || (r && allocated)) {
657 /*
658 * IB contains a destroy msg or we have allocated an
659 * handle and got an error, anyway free the handle
660 */
630 for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i) 661 for (i = 0; i < RADEON_MAX_VCE_HANDLES; ++i)
631 atomic_cmpxchg(&p->rdev->vce.handles[i], handle, 0); 662 atomic_cmpxchg(&p->rdev->vce.handles[i], handle, 0);
632 } 663 }
633 664
634 return 0; 665 return r;
635} 666}
636 667
637/** 668/**