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/drm/radeon | |
| 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/drm/radeon')
| -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 | /** |
