Skip to content

repo rename -> invalid memory access pattern #8675

Description

@thor314

gh --version
gh version 2.4.0+dfsg1 (2022-03-23 Ubuntu 2.4.0+dfsg1-2)
https://github.com/cli/cli/releases/latest

(that's about 2 years ago so this may already be fixed)

I attempted to rename a repo, cron -> .cron. The repo was called .cron at one point, and I intended to move the repo back to the initial name.

bug discovery:

~/.cron main ❯ gh repo rename .cron
? Rename thor314/.cron to .cron? Yes
✓ Renamed repository thor314/.cron
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xdf88ac]

goroutine 1 [running]:
github.com/cli/cli/v2/pkg/cmd/repo/rename.renameRun(0xc0005c5c00)
	github.com/cli/cli/v2/pkg/cmd/repo/rename/rename.go:133 +0x5ac
github.com/cli/cli/v2/pkg/cmd/repo/rename.NewCmdRename.func1(0xc000306280?, {0xc0004c8750, 0x1, 0x1?})
	github.com/cli/cli/v2/pkg/cmd/repo/rename/rename.go:67 +0x19c
github.com/spf13/cobra.(*Command).execute(0xc000306280, {0xc0004c8730, 0x1, 0x1})
	github.com/spf13/cobra/command.go:856 +0x67c
github.com/spf13/cobra.(*Command).ExecuteC(0xc0001fc500)
	github.com/spf13/cobra/command.go:974 +0x3b4
main.mainRun()
	github.com/cli/cli/v2/cmd/gh/main.go:203 +0xd0d
main.main()
	github.com/cli/cli/v2/cmd/gh/main.go:46 +0x19

Activity

  1. thor314 commented on Feb 8, 2024

    @thor314
    Author

    updated version. Bug persists.
    gh version 2.43.1 (2024-01-31)
    https://github.com/cli/cli/releases/tag/v2.43.1

  2. thor314 commented on Feb 8, 2024

    @thor314
    Author

    updated version. Bug persists.
    gh version 2.43.1 (2024-01-31)
    https://github.com/cli/cli/releases/tag/v2.43.1

  3. andyfeller commented on Feb 12, 2024

    @andyfeller
    Contributor

    @thor314 : thanks for opening this issue! 🙇

    gh --version
    gh version 2.4.0+dfsg1 (2022-03-23 Ubuntu 2.4.0+dfsg1-2) https://github.com/cli/cli/releases/latest

    (that's about 2 years ago so this may already be fixed)

    Could you re-run this using GH_DEBUG=api env var and provide the terminal output here please?

    Testing this out locally using latest gh going from cron to .cron to .whatever to cron and finally .cron seemingly work:

    $ gh version
    gh version 2.43.1 (2024-01-31)
    https://github.com/cli/cli/releases/tag/v2.43.1
    
    # Test .<nameA> to .<nameB>
    $ gh repo rename .whatever
    ? Rename tinyfists/.cron to .whatever? Yes
    ✓ Renamed repository tinyfists/.whatever
    ✓ Updated the "origin" remote
    
    # Test .<nameB> to .<nameB>
    $ gh repo rename .whatever
    ? Rename tinyfists/.whatever to .whatever? Yes
    ✓ Renamed repository tinyfists/.whatever
    ✓ Updated the "origin" remote
    
    # Test .<nameB> to <nameA>
    $ gh repo rename cron     
    ? Rename tinyfists/.whatever to cron? Yes
    ✓ Renamed repository tinyfists/cron
    ✓ Updated the "origin" remote
    
    # Test <nameA> to .<nameA>
    $ gh repo rename .cron
    ? Rename tinyfists/cron to .cron? Yes
    ✓ Renamed repository tinyfists/.cron
    ✓ Updated the "origin" remote
  4. added
    more-info-neededMore info needed from user/contributor
    gh-reporelating to the gh repo command
    and removed on Feb 12, 2024
  5. thor314 commented on Feb 14, 2024

    @thor314
    Author

    Hmm. I was able to reproduce the issue, but for whatever reason, running the command with -y seems to have avoided the bug, and I can no longer reproduce the issue.

    I was able to capture the following with GH_DEBUG enabled:

    ~ ❯ cd .cron && GH_DEBUG=api gh repo rename .cron
    [git remote -v]
    [git config --get-regexp ^remote\..*\.gh-resolved$]
    * Request at 2024-02-13 20:27:00.053504767 -0800 PST m=+0.037150821
    * Request to https://api.github.com/graphql
    > POST /graphql HTTP/1.1
    > Host: api.github.com
    > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
    > Authorization: token ████████████████████
    > Content-Length: 386
    > Content-Type: application/json; charset=utf-8
    > Graphql-Features: merge_queue
    > Time-Zone: America/Los_Angeles
    > User-Agent: GitHub CLI 2.43.1
    
    GraphQL query:
    fragment repo on Repository {
        id
        name
        owner { login }
        viewerPermission
        defaultBranchRef {
          name
        }
        isPrivate
      }
      query RepositoryNetwork {
        viewer { login }
    
        repo_000: repository(owner: "thor314", name: "cron") {
          ...repo
          parent {
            ...repo
          }
        }
    
      }
    GraphQL variables: null
    
    < HTTP/2.0 200 OK
    < Access-Control-Allow-Origin: *
    < Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
    < Content-Security-Policy: default-src 'none'
    < Content-Type: application/json; charset=utf-8
    < Date: Wed, 14 Feb 2024 04:27:00 GMT
    < Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
    < Server: GitHub.com
    < Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
    < Vary: Accept-Encoding, Accept, X-Requested-With
    < X-Accepted-Oauth-Scopes: repo
    < X-Content-Type-Options: nosniff
    < X-Frame-Options: deny
    < X-Github-Media-Type: github.merge-info-preview; param=nebula-preview; format=json
    < X-Github-Request-Id: 94AA:47BB:354EF40:36D9747:65CC4114
    < X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
    < X-Oauth-Scopes: admin:public_key, admin:ssh_signing_key, delete_repo, gist, read:org, repo
    < X-Ratelimit-Limit: 5000
    < X-Ratelimit-Remaining: 4999
    < X-Ratelimit-Reset: 1707888420
    < X-Ratelimit-Resource: graphql
    < X-Ratelimit-Used: 1
    < X-Xss-Protection: 0
    
    {
      "data": {
        "viewer": {
          "login": "thor314"
        },
        "repo_000": {
          "id": "R_kgDOIYiKcA",
          "name": ".cron",
          "owner": {
            "login": "thor314"
          },
          "viewerPermission": "ADMIN",
          "defaultBranchRef": {
            "name": "main"
          },
          "isPrivate": false,
          "parent": null
        }
      }
    }
    
    * Request took 320.195337ms
    ? Rename thor314/.cron to .cron? Yes
    * Request at 2024-02-13 20:27:03.184540981 -0800 PST m=+3.168187073
    * Request to https://api.github.com/repos/thor314/.cron
    > PATCH /repos/thor314/.cron HTTP/1.1
    > Host: api.github.com
    > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
    > Authorization: token ████████████████████
    > Content-Length: 17
    > Content-Type: application/json; charset=utf-8
    > Time-Zone: America/Los_Angeles
    > User-Agent: GitHub CLI 2.43.1
    
    {
      "name": ".cron"
    }
    
    < HTTP/2.0 200 OK
    < Access-Control-Allow-Origin: *
    < Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
    < Cache-Control: private, max-age=60, s-maxage=60
    < Content-Security-Policy: default-src 'none'
    < Content-Type: application/json; charset=utf-8
    < Date: Wed, 14 Feb 2024 04:27:03 GMT
    < Etag: W/"85119dd612bec76ae90bf7032fb3d3bf6ec61e7ceb6c3fc1a2eb504c4ed8dd10"
    < Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
    < Server: GitHub.com
    < Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
    < Vary: Accept, Authorization, Cookie, X-GitHub-OTP
    < Vary: Accept-Encoding, Accept, X-Requested-With
    < X-Accepted-Oauth-Scopes:
    < X-Content-Type-Options: nosniff
    < X-Frame-Options: deny
    < X-Github-Api-Version-Selected: 2022-11-28
    < X-Github-Media-Type: github.merge-info-preview; param=nebula-preview; format=json
    < X-Github-Request-Id: 94AA:47BB:354F39D:36D9BC2:65CC4114
    < X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
    < X-Oauth-Scopes: admin:public_key, admin:ssh_signing_key, delete_repo, gist, read:org, repo
    < X-Ratelimit-Limit: 5000
    < X-Ratelimit-Remaining: 4990
    < X-Ratelimit-Reset: 1707887725
    < X-Ratelimit-Resource: core
    < X-Ratelimit-Used: 10
    < X-Xss-Protection: 0
    
    {
      "id": 562596464,
      "node_id": "R_kgDOIYiKcA",
      "name": ".cron",
      "full_name": "thor314/.cron",
      "private": false,
      "owner": {
        "login": "thor314",
        "id": 7041313,
        "node_id": "MDQ6VXNlcjcwNDEzMTM=",
        "avatar_url": "https://avatars.githubusercontent.com/u/7041313?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/thor314",
        "html_url": "https://github.com/thor314",
        "followers_url": "https://api.github.com/users/thor314/followers",
        "following_url": "https://api.github.com/users/thor314/following{/other_user}",
        "gists_url": "https://api.github.com/users/thor314/gists{/gist_id}",
        "starred_url": "https://api.github.com/users/thor314/starred{/owner}{/repo}",
        "subscriptions_url": "https://api.github.com/users/thor314/subscriptions",
        "organizations_url": "https://api.github.com/users/thor314/orgs",
        "repos_url": "https://api.github.com/users/thor314/repos",
        "events_url": "https://api.github.com/users/thor314/events{/privacy}",
        "received_events_url": "https://api.github.com/users/thor314/received_events",
        "type": "User",
        "site_admin": false
      },
      "html_url": "https://github.com/thor314/.cron",
      "description": null,
      "fork": false,
      "url": "https://api.github.com/repos/thor314/.cron",
      "forks_url": "https://api.github.com/repos/thor314/.cron/forks",
      "keys_url": "https://api.github.com/repos/thor314/.cron/keys{/key_id}",
      "collaborators_url": "https://api.github.com/repos/thor314/.cron/collaborators{/collaborator}",
      "teams_url": "https://api.github.com/repos/thor314/.cron/teams",
      "hooks_url": "https://api.github.com/repos/thor314/.cron/hooks",
      "issue_events_url": "https://api.github.com/repos/thor314/.cron/issues/events{/number}",
      "events_url": "https://api.github.com/repos/thor314/.cron/events",
      "assignees_url": "https://api.github.com/repos/thor314/.cron/assignees{/user}",
      "branches_url": "https://api.github.com/repos/thor314/.cron/branches{/branch}",
      "tags_url": "https://api.github.com/repos/thor314/.cron/tags",
      "blobs_url": "https://api.github.com/repos/thor314/.cron/git/blobs{/sha}",
      "git_tags_url": "https://api.github.com/repos/thor314/.cron/git/tags{/sha}",
      "git_refs_url": "https://api.github.com/repos/thor314/.cron/git/refs{/sha}",
      "trees_url": "https://api.github.com/repos/thor314/.cron/git/trees{/sha}",
      "statuses_url": "https://api.github.com/repos/thor314/.cron/statuses/{sha}",
      "languages_url": "https://api.github.com/repos/thor314/.cron/languages",
      "stargazers_url": "https://api.github.com/repos/thor314/.cron/stargazers",
      "contributors_url": "https://api.github.com/repos/thor314/.cron/contributors",
      "subscribers_url": "https://api.github.com/repos/thor314/.cron/subscribers",
      "subscription_url": "https://api.github.com/repos/thor314/.cron/subscription",
      "commits_url": "https://api.github.com/repos/thor314/.cron/commits{/sha}",
      "git_commits_url": "https://api.github.com/repos/thor314/.cron/git/commits{/sha}",
      "comments_url": "https://api.github.com/repos/thor314/.cron/comments{/number}",
      "issue_comment_url": "https://api.github.com/repos/thor314/.cron/issues/comments{/number}",
      "contents_url": "https://api.github.com/repos/thor314/.cron/contents/{+path}",
      "compare_url": "https://api.github.com/repos/thor314/.cron/compare/{base}...{head}",
      "merges_url": "https://api.github.com/repos/thor314/.cron/merges",
      "archive_url": "https://api.github.com/repos/thor314/.cron/{archive_format}{/ref}",
      "downloads_url": "https://api.github.com/repos/thor314/.cron/downloads",
      "issues_url": "https://api.github.com/repos/thor314/.cron/issues{/number}",
      "pulls_url": "https://api.github.com/repos/thor314/.cron/pulls{/number}",
      "milestones_url": "https://api.github.com/repos/thor314/.cron/milestones{/number}",
      "notifications_url": "https://api.github.com/repos/thor314/.cron/notifications{?since,all,participating}",
      "labels_url": "https://api.github.com/repos/thor314/.cron/labels{/name}",
      "releases_url": "https://api.github.com/repos/thor314/.cron/releases{/id}",
      "deployments_url": "https://api.github.com/repos/thor314/.cron/deployments",
      "created_at": "2022-11-06T20:28:20Z",
      "updated_at": "2024-01-31T00:43:00Z",
      "pushed_at": "2024-02-13T22:18:33Z",
      "git_url": "git://github.com/thor314/.cron.git",
      "ssh_url": "[email protected]:thor314/.cron.git",
      "clone_url": "https://github.com/thor314/.cron.git",
      "svn_url": "https://github.com/thor314/.cron",
      "homepage": "",
      "size": 159,
      "stargazers_count": 0,
      "watchers_count": 0,
      "language": "Shell",
      "has_issues": true,
      "has_projects": true,
      "has_downloads": true,
      "has_wiki": true,
      "has_pages": false,
      "has_discussions": false,
      "forks_count": 0,
      "mirror_url": null,
      "archived": false,
      "disabled": false,
      "open_issues_count": 0,
      "license": null,
      "allow_forking": true,
      "is_template": false,
      "web_commit_signoff_required": false,
      "topics": [],
      "visibility": "public",
      "forks": 0,
      "open_issues": 0,
      "watchers": 0,
      "default_branch": "main",
      "permissions": {
        "admin": true,
        "maintain": true,
        "push": true,
        "triage": true,
        "pull": true
      },
      "allow_squash_merge": true,
      "allow_merge_commit": true,
      "allow_rebase_merge": true,
      "allow_auto_merge": false,
      "delete_branch_on_merge": false,
      "allow_update_branch": false,
      "use_squash_pr_title_as_default": false,
      "squash_merge_commit_message": "COMMIT_MESSAGES",
      "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
      "merge_commit_message": "PR_TITLE",
      "merge_commit_title": "MERGE_MESSAGE",
      "security_and_analysis": {
        "secret_scanning": {
          "status": "disabled"
        },
        "secret_scanning_push_protection": {
          "status": "disabled"
        },
        "dependabot_security_updates": {
          "status": "disabled"
        },
        "secret_scanning_validity_checks": {
          "status": "disabled"
        }
      },
      "network_count": 0,
      "subscribers_count": 1
    }
    
    * Request took 194.792253ms
    ✓ Renamed repository thor314/.cron
    [git remote -v]
    [git config --get-regexp ^remote\..*\.gh-resolved$]
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1259543]
    
    goroutine 1 [running]:
    github.com/cli/cli/v2/pkg/cmd/repo/rename.renameRun(0xc0003050e0)
    	/home/runner/work/cli/cli/pkg/cmd/repo/rename/rename.go:138 +0x4a3
    github.com/cli/cli/v2/pkg/cmd/repo/rename.NewCmdRename.func1(0xc00053d200?, {0xc0007c6db0, 0x1, 0x14fab9d?})
    	/home/runner/work/cli/cli/pkg/cmd/repo/rename/rename.go:77 +0x1a5
    github.com/spf13/cobra.(*Command).execute(0xc000552300, {0xc0007c6d90, 0x1, 0x1})
    	/home/runner/go/pkg/mod/github.com/spf13/[email protected]/command.go:916 +0x87c
    github.com/spf13/cobra.(*Command).ExecuteC(0xc000004900)
    	/home/runner/go/pkg/mod/github.com/spf13/[email protected]/command.go:1044 +0x3a5
    github.com/spf13/cobra.(*Command).ExecuteContextC(...)
    	/home/runner/go/pkg/mod/github.com/spf13/[email protected]/command.go:977
    main.mainRun()
    	/home/runner/work/cli/cli/cmd/gh/main.go:119 +0x5db
    main.main()
    	/home/runner/work/cli/cli/cmd/gh/main.go:46 +0x13
    

    But with the -y flag, bug fails to appear and can no longer be reproduced:

    ~/.cron main ?1 ❯ gh repo rename .cron -y &> gh.log
    ~/.cron main ?1 ❯ bat --theme=zenburn --style=plain gh.log
    ~/.cron main ?1 ❯ gh repo rename .cron -y
    ✓ Renamed repository thor314/.cron
    ✓ Updated the "origin" remote
    ~/.cron main ?1 ❯ hub browse
    ~/.cron main ?1 ❯ gh repo rename cron
    ? Rename thor314/.cron to cron? Yes
    ✓ Renamed repository thor314/cron
    ✓ Updated the "origin" remote
    ~/.cron main ?1 ❯ gh repo rename .cron
    ? Rename thor314/cron to .cron? Yes
    ✓ Renamed repository thor314/.cron
    ✓ Updated the "origin" remote
    
  6. williammartin commented on Feb 15, 2024

    @williammartin
    Member

    I haven't got the time right now to investigate this further but this line is not idiomatic (we shouldn't be using remote if err != nil) and that's where the nil pointer is coming from:

    fmt.Fprintf(opts.IO.ErrOut, "%s Warning: unable to update remote %q: %v\n", cs.WarningIcon(), remote.Name, err)

    I'm not sure what's happening before because we don't print the error.

    @thor314 if you're up for it can you just change this line:

    fmt.Fprintf(opts.IO.ErrOut, "%s Warning: unable to update remote %q: %v\n", cs.WarningIcon(), remote.Name, err)

    to

    fmt.Fprintf(opts.IO.ErrOut, "%s Warning: unable to update remote: %v\n", cs.WarningIcon(), err)

    You can use make to build and then ./bin/gh to run the build binary.

  7. williammartin commented on Feb 15, 2024

    @williammartin
    Member

    I can't see any possible reason that -y would impact this, it just determines whether to present a prompt or not. I wonder whether there was some other state change.

    if opts.DoConfirm {
    var confirmed bool
    if confirmed, err = opts.Prompter.Confirm(fmt.Sprintf(
    "Rename %s to %s?", ghrepo.FullName(currRepo), newRepoName), false); err != nil {
    return err
    }
    if !confirmed {
    return nil
    }
    }

  8. williammartin commented on Feb 15, 2024

    @williammartin
    Member

    Doh, I see that you aren't able to reproduce this anymore. Well what I'd suggest is that we stick a help wanted label on here and someone can open a PR to put something more sensible in the log message that won't try to deref a nil, then maybe we'll see the right error in future.

  9. added and removed
    more-info-neededMore info needed from user/contributor
    on Feb 15, 2024
  10. thor314 commented on Feb 15, 2024

    @thor314
    Author

    yeah sorry I can't be more helpful. Bug has scuttled under a proverbial couch for now. Good luck and thanks.

  11. babakks commented on Apr 1, 2024

    @babakks
    Member

    @williammartin I just submitted a PR to fix this.

  12. babakks commented on Apr 1, 2024

    @babakks
    Member

    Playing around a bit, at last I could reproduce the panic with these steps:

    1. Create a new repo, clone, and cd into it:
      gh repo create gh-some-repo --private --add-readme && \
      gh repo clone gh-some-repo && \
      cd gh-some-repo
      
    2. Rename the repo (this will be successful and will also update the origin remote):
      gh repo rename gh-some-repo-renamed
      
    3. Change the origin remote back to what it was before renaming the repo:
      git remote set-url origin [email protected]:babakks/gh-some-repo.git
      
    4. Try renaming the repo back to its initial name (this can be with or without the --yes option):
      gh repo rename gh-some-repo --yes
      
    5. Should see the panic log.

    Note that, if you retry the last command (step 4), this time no panic will happen.

    The problem

    When we call updateRemote here:

    remote, err := updateRemote(currRepo, newRepo, opts)

    it returns a nil remote and an error saying no matching remote found, which is actually returned from the FindByRepo method when it was looking for a remote pointing to the old name (i.e., gh-some-repo-renamed) but couldn't find any.

  13. babakks commented on Apr 1, 2024

    @babakks
    Member

    @williammartin I just realized that there's already a PR (#8888) submitted for this issue by @satoqz. I didn't notice because it wasn't linked to the issue.

  14. satoqz commented on Apr 1, 2024

    @satoqz
    Contributor

    @babakks thanks for the ping, turns out I didn't find this issue before opening my PR 😅

    Anyways, @williammartin I can confirm the reproducibility of this issue, plus the fix that you proposed (which incidentally is the same change that I ended up with while debugging it myself) does resolve it, see #8888.

  15. williammartin commented on Apr 2, 2024

    @williammartin
    Member

    @babakks super work on the reproduction. I can confirm this, and I also understand why it doesn't panic the second time (because the API rename succeeds the first time, thus the second time we're looking for a remote with the name that already exists in our git remotes).

    It's a shame (and funny) that you both happened to pick this up at the same time. Since I have the wisdom of Solomon I'm going to split the PR by:

    1. Merging @satoqz PR since it was opened first
    2. Asking @babakks to rebase and include the change they have in remote.go which is handy

    Thanks so much to you both for working on this!

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggh-reporelating to the gh repo commandhelp wantedContributions welcome

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions