diff options
author | Christian König <christian.koenig@amd.com> | 2015-05-07 09:19:22 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2015-05-07 11:00:16 -0400 |
commit | 29c63fe22a17c64e54016040cd882481bd45ee5a (patch) | |
tree | 7f8495f172b56791c0b06b65225e0e465b91efa0 /drivers/gpu | |
parent | 247c405098ab731ad9b58971e2cfbab116b54b45 (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.c | 65 |
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 | */ |
500 | int radeon_vce_validate_handle(struct radeon_cs_parser *p, uint32_t handle) | 501 | static 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) | |||
529 | int radeon_vce_cs_parse(struct radeon_cs_parser *p) | 539 | int 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 | |||
655 | out: | ||
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 | /** |