Skip to content

[Deepin-Kernel-SIG] [linux 6.18.y] [FROMLIST] [Security] drm/gem: Try to fix change_handle ioctl, attempt 4#1832

Open
Avenger-285714 wants to merge 1 commit into
deepin-community:linux-6.18.yfrom
Avenger-285714:gem-ioctl-6.18
Open

[Deepin-Kernel-SIG] [linux 6.18.y] [FROMLIST] [Security] drm/gem: Try to fix change_handle ioctl, attempt 4#1832
Avenger-285714 wants to merge 1 commit into
deepin-community:linux-6.18.yfrom
Avenger-285714:gem-ioctl-6.18

Conversation

@Avenger-285714

@Avenger-285714 Avenger-285714 commented Jun 9, 2026

Copy link
Copy Markdown
Member

[airlied: just added some comments on how to reenable] On-list because the cat is out of the bag and we're clearly not good enough to figure this out in private. The story thus far:

5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle") tried to fix a race condition between the gem_close and gem_change_handle ioctls, but got a few things wrong:

  • There's a confusion with the local variable handle, which is actually the new handle, and so the two-stage trick was actually applied to the wrong idr slot. 7164d78559b0 ("drm/gem: fix race between change_handle and handle_delete") tried to fix that by adding yet another code block, but forgot to add the error handling. Which meant we now have two paths, both kinda wrong.

  • dc366607c41c ("drm: Replace old pointer to new idr") tried to apply another fix, but inconsistently, again because of the handle confusion

    • this would be the right fix (kinda, somewhat, it's a mess) if we'd do the two-stage approach for the new handle. Except that wasn't the intent of the original fix.

We also didn't have an igt merged for the original ioctl, which is a big no-go. This was attempted to address off-list in the original bugfix, and amd QA people claimed the bug was fixed now. Very clearly that's not the case. Here's my attempt to sort this out:

  • Rename the local variable to new_handle, the old aliasing with args->handle is just too dangerously confusing.

  • Merge the gem obj lookup with the two-stage idr_replace so that we avoid getting ourselves confused there.

  • This means we don't have a surplus temporary reference anymore, only an inherited from the idr. A concurrent gem_close on the new_handle could steal that. Fix that with the same two-stage approach create_tail uses. This is a bit overkill as documented in the comment, but I also don't trust my ability to understand this all correctly, so go with the established pattern we have from other ioctls instead for maximum paranoia.

  • Adjust error paths. I've tried to make the error and success paths common, because they are identical except for which handle is removed and on which we call idr_replace to (re)install the object again. But that made things messier to read, so I've left it at the more verbose version, which unfortunately hides the symmetry in the entire code flow a bit.

  • While at it, also replace the 7 space indent with 1 tab.

And finally, because I flat out don't trust my abilities here at all anymore:

  • Disable the ioctl until we have the igt situation and everything else sorted out on-list and with full consensus.

v2:

Sashiko noticed that I didn't handle the error path for idr_replace correctly, it must be checked with IS_ERR_OR_NULL like in gem_handle_delete. So yeah, definitely should just the existing paths 1:1 because this is endless amounts of tricky.

Also add the Fixes: line for the original ioctl, I forgot that too.

Reported-by: DARKNAVY (@DarkNavyOrg) vr@darknavy.com

Fixes: dc366607c41c ("drm: Replace old pointer to new idr")
Cc: syzbot+d7c9eed171647e421013@syzkaller.appspotmail.com
Cc: stable@vger.kernel.org
Cc: Edward Adam Davis eadavis@qq.com
Cc: Dave Airlie airlied@redhat.com
Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com
Cc: Maxime Ripard mripard@kernel.org
Cc: Thomas Zimmermann tzimmermann@suse.de
Fixes: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle")
Cc: David Francis David.Francis@amd.com
Cc: Puttimet Thammasaeng pwn8official@gmail.com
Cc: Christian Koenig Christian.Koenig@amd.com
Fixes: 7164d78559b0 ("drm/gem: fix race between change_handle and handle_delete")
Cc: Zhenghang Xiao kipreyyy@gmail.com
Fixes: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle")
Reviewed-by: David Francis David.Francis@amd.com

Link: https://patch.msgid.link/20260604194437.1725314-1-simona.vetter@ffwll.ch
[WangYuli: Fix conflicts]
Link: https://gitlab.freedesktop.org/drm/kernel/-/commit/1a4f03d22fb655e5f192244fb2c87d8066fcfca2

Summary by Sourcery

Disable the drm GEM change_handle ioctl for security and process reasons while tightening its internal handle management logic to prevent races and inconsistencies.

Bug Fixes:

  • Fix race-prone GEM handle change logic by correctly managing idr allocations, replacements, and error handling for old and new handles.

Enhancements:

  • Clarify and simplify drm_gem_change_handle_ioctl by renaming variables, using safer idr_replace patterns, and documenting the requirements for any future re-enablement of the ioctl.

[airlied: just added some comments on how to reenable]
On-list because the cat is out of the bag and we're clearly not good
enough to figure this out in private. The story thus far:

5e28b7b94408 ("drm: Set old handle to NULL before prime swap in
change_handle") tried to fix a race condition between the gem_close and
gem_change_handle ioctls, but got a few things wrong:

- There's a confusion with the local variable handle, which is actually
  the new handle, and so the two-stage trick was actually applied to the
  wrong idr slot. 7164d78559b0 ("drm/gem: fix race between
  change_handle and handle_delete") tried to fix that by adding yet
  another code block, but forgot to add the error handling. Which meant
  we now have two paths, both kinda wrong.

- dc366607c41c ("drm: Replace old pointer to new idr") tried to apply
  another fix, but inconsistently, again because of the handle confusion
  - this would be the right fix (kinda, somewhat, it's a mess) if we'd
  do the two-stage approach for the new handle. Except that wasn't the
  intent of the original fix.

We also didn't have an igt merged for the original ioctl, which is a big
no-go. This was attempted to address off-list in the original bugfix,
and amd QA people claimed the bug was fixed now. Very clearly that's not
the case. Here's my attempt to sort this out:

- Rename the local variable to new_handle, the old aliasing with
  args->handle is just too dangerously confusing.

- Merge the gem obj lookup with the two-stage idr_replace so that we
  avoid getting ourselves confused there.

- This means we don't have a surplus temporary reference anymore, only
  an inherited from the idr. A concurrent gem_close on the new_handle
  could steal that. Fix that with the same two-stage approach
  create_tail uses. This is a bit overkill as documented in the comment,
  but I also don't trust my ability to understand this all correctly, so
  go with the established pattern we have from other ioctls instead for
  maximum paranoia.

- Adjust error paths. I've tried to make the error and success paths
  common, because they are identical except for which handle is removed
  and on which we call idr_replace to (re)install the object again. But
  that made things messier to read, so I've left it at the more verbose
  version, which unfortunately hides the symmetry in the entire code
  flow a bit.

- While at it, also replace the 7 space indent with 1 tab.

And finally, because I flat out don't trust my abilities here at all
anymore:

- Disable the ioctl until we have the igt situation and everything else
  sorted out on-list and with full consensus.

v2:

Sashiko noticed that I didn't handle the error path for idr_replace
correctly, it must be checked with IS_ERR_OR_NULL like in
gem_handle_delete. So yeah, definitely should just the existing paths
1:1 because this is endless amounts of tricky.

Also add the Fixes: line for the original ioctl, I forgot that too.

Reported-by: DARKNAVY (@DarkNavyOrg) <vr@darknavy.com>
Signed-off-by: Simona Vetter <simona.vetter@ffwll.ch>
Fixes: dc366607c41c ("drm: Replace old pointer to new idr")
Cc: syzbot+d7c9eed171647e421013@syzkaller.appspotmail.com
Cc: stable@vger.kernel.org
Cc: Edward Adam Davis <eadavis@qq.com>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle")
Cc: David Francis <David.Francis@amd.com>
Cc: Puttimet Thammasaeng <pwn8official@gmail.com>
Cc: Christian Koenig <Christian.Koenig@amd.com>
Fixes: 7164d78559b0 ("drm/gem: fix race between change_handle and handle_delete")
Cc: Zhenghang Xiao <kipreyyy@gmail.com>
Fixes: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle")
Reviewed-by: David Francis <David.Francis@amd.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Link: https://patch.msgid.link/20260604194437.1725314-1-simona.vetter@ffwll.ch
[WangYuli: Fix conflicts]
Link: https://gitlab.freedesktop.org/drm/kernel/-/commit/1a4f03d22fb655e5f192244fb2c87d8066fcfca2
Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
@sourcery-ai

sourcery-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Reviewer's Guide

Disables the insecure DRM GEM change_handle ioctl and rewrites its implementation to use a safer two-stage idr replacement pattern, renaming variables and aligning error handling with other GEM handle paths.

Sequence diagram for updated GEM change_handle ioctl dispatch

sequenceDiagram
    participant Userspace
    participant DRM_core
    participant drm_invalid_op

    Userspace->>DRM_core: DRM_IOCTL_GEM_CHANGE_HANDLE
    DRM_core->>drm_invalid_op: drm_invalid_op
    drm_invalid_op-->>DRM_core: -EINVAL (invalid ioctl)
    DRM_core-->>Userspace: ioctl error (-EINVAL)
Loading

Flow diagram for new drm_gem_change_handle_ioctl idr handling

flowchart TD
    A[Start drm_gem_change_handle_ioctl] --> B[drm_core_check_feature]
    B -->|unsupported| Z[Return -EOPNOTSUPP]
    B -->|supported| C[args->handle == new_handle?]
    C -->|yes| Z2[Return 0]
    C -->|no| D[mutex_lock prime.lock]
    D --> E[spin_lock table_lock]
    E --> F[idr_alloc object_idr NULL new_handle]
    F -->|ret < 0| G[spin_unlock table_lock]
    G --> H[goto out_unlock]
    F -->|ret >= 0| I[obj = idr_replace object_idr NULL args->handle]
    I -->|IS_ERR_OR_NULL obj| J[idr_remove object_idr new_handle]
    J --> K[spin_unlock table_lock]
    K --> H
    I -->|obj valid| L[spin_unlock table_lock]
    L --> M[obj->dma_buf?]
    M -->|yes| N[drm_prime_add_buf_handle prime obj->dma_buf new_handle]
    N -->|ret < 0| O[spin_lock table_lock]
    O --> P[idr_remove object_idr new_handle]
    P --> Q[spin_unlock table_lock]
    Q --> H
    M -->|no or success| R[spin_lock table_lock]
    R --> S[idr_remove object_idr args->handle]
    S --> T[obj = idr_replace object_idr obj new_handle]
    T --> U[spin_unlock table_lock]
    U --> V[WARN_ON obj != NULL]
    V --> H
    H[out_unlock] --> W[mutex_unlock prime.lock]
    W --> X[Return ret]
Loading

File-Level Changes

Change Details Files
Harden and effectively disable drm_gem_change_handle_ioctl by switching it to drm_invalid_op and restructuring its internal handle remapping logic to use a two-stage idr_replace pattern with safer error handling.
  • Add detailed comments in drm_gem.c documenting that drm_gem_change_handle_ioctl is disabled for security reasons and what is required to re-enable it.
  • Rename the local handle variable to new_handle and early-return when old and new handles are equal.
  • Change the idr allocation to first allocate a NULL entry for the new handle, then atomically swap out the old handle’s object using idr_replace, checking the result with IS_ERR_OR_NULL and reverting on failure.
  • Adjust prime handle bookkeeping to use the new_handle and update error paths to remove the newly allocated idr slot and drop locks correctly on failures.
  • Remove the final drm_gem_object_put() and instead rely on the idr reference semantics, updating WARN_ON logic to assert that the new_handle idr slot was previously empty.
  • In drm_ioctl.c, replace the DRM_IOCTL_GEM_CHANGE_HANDLE handler from drm_gem_change_handle_ioctl to drm_invalid_op and add a comment pointing reviewers to drm_gem.c for rationale.
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/drm_ioctl.c

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="drivers/gpu/drm/drm_gem.c" line_range="1010-1011" />
<code_context>
-	       ret = -ENOENT;
-	       goto out_unlock;
-       }
+	obj = idr_replace(&file_priv->object_idr, NULL, args->handle);
+	if (IS_ERR_OR_NULL(obj)) {
+		idr_remove(&file_priv->object_idr, new_handle);
+		spin_unlock(&file_priv->table_lock);
</code_context>
<issue_to_address>
**issue (bug_risk):** New handle change path can leave the old handle mapped to NULL if drm_prime_add_buf_handle() fails.

On failure, the new_handle entry is removed but the original args->handle mapping is not restored, leaving a NULL entry in the idr and dropping its GEM association. This changes behavior from the prior implementation, which preserved the old mapping until prime handle setup succeeded. Please either restore the original obj at args->handle on failure or reorder operations so args->handle is only replaced after drm_prime_add_buf_handle() succeeds.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread drivers/gpu/drm/drm_gem.c
Comment on lines +1010 to +1011
obj = idr_replace(&file_priv->object_idr, NULL, args->handle);
if (IS_ERR_OR_NULL(obj)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): New handle change path can leave the old handle mapped to NULL if drm_prime_add_buf_handle() fails.

On failure, the new_handle entry is removed but the original args->handle mapping is not restored, leaving a NULL entry in the idr and dropping its GEM association. This changes behavior from the prior implementation, which preserved the old mapping until prime handle setup succeeded. Please either restore the original obj at args->handle on failure or reorder operations so args->handle is only replaced after drm_prime_add_buf_handle() succeeds.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Disables the DRM GEM CHANGE_HANDLE ioctl at the core ioctl dispatcher due to unresolved security/race concerns, while updating drm_gem_change_handle_ioctl() with clearer variable naming and revised idr/prime bookkeeping in preparation for a future, properly-tested re-enable.

Changes:

  • Route DRM_IOCTL_GEM_CHANGE_HANDLE to drm_invalid_op (ioctl disabled).
  • Add a detailed comment block describing why the ioctl is disabled and prerequisites for re-enabling.
  • Rework drm_gem_change_handle_ioctl() internals (variable rename to new_handle, revised idr_alloc/idr_replace flow, adjusted error paths, indentation fixes).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
drivers/gpu/drm/drm_ioctl.c Disables DRM_IOCTL_GEM_CHANGE_HANDLE by mapping it to drm_invalid_op and adds a reference comment.
drivers/gpu/drm/drm_gem.c Documents disablement and refactors drm_gem_change_handle_ioctl() handle/idr management logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread drivers/gpu/drm/drm_gem.c
Comment on lines 1023 to 1027
if (ret < 0) {
spin_lock(&file_priv->table_lock);
idr_remove(&file_priv->object_idr, handle);
idr_remove(&file_priv->object_idr, new_handle);
spin_unlock(&file_priv->table_lock);
goto out_unlock;
Comment thread drivers/gpu/drm/drm_gem.c
Comment on lines 992 to +996
if (args->new_handle > INT_MAX)
return -EINVAL;
handle = args->new_handle;
new_handle = args->new_handle;

obj = drm_gem_object_lookup(file_priv, args->handle);
if (!obj)
return -ENOENT;

if (args->handle == handle) {
ret = 0;
goto out;
}
if (args->handle == new_handle)
DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_CHANGE_HANDLE, drm_gem_change_handle_ioctl, DRM_RENDER_ALLOW),
/* see drm_gem.c:drm_gem_change_handle_ioctl for why this is invalid */
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please ask for approval from avenger-285714. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants