cli/command/service: fix panic when removing duplicate values - #7145
Open
winklemad wants to merge 1 commit into
Open
cli/command/service: fix panic when removing duplicate values#7145winklemad wants to merge 1 commit into
winklemad wants to merge 1 commit into
Conversation
Both makeEnv and updateHosts removed elements from a slice while ranging
over that same slice. The range expression is evaluated once, so after a
removal the loop keeps using the original length: it reads stale elements
that shifted down, skips a live element, and can slice past the end of the
shrunken slice, which panics.
In makeEnv, the "no update required" continue also applied to the inner
loop instead of skipping the re-append, so an env-var passed twice with
the same value was stored twice. A third occurrence with a different value
then tried to remove both entries and panicked:
docker service create --env A=1 --env A=1 --env A=2 --name repro nginx
panic: runtime error: slice bounds out of range [2:1]
The same happens with an env-file that lists a variable twice, and the
panic occurs before any API call, so no daemon is needed to hit it.
updateHosts has the same problem when a hostname is listed more than once
in a single entry: --host-rm either leaves a copy behind or panics with
"slice bounds out of range". That needs a spec written through the API or
swarmkit directly, as the CLI does not produce such entries itself, so it
is less likely to be hit in practice.
Use slices.DeleteFunc for both, which removes every match in one pass, and
add tests for makeEnv, which had no coverage.
Signed-off-by: Madan Kumar <winklemad@outlook.com>
There was a problem hiding this comment.
Pull request overview
Fixes a panic in service env/hosts option processing by replacing unsafe in-place slice deletions during range iteration with safe slice filtering, and adds regression tests to cover the duplicate-input cases.
Changes:
- Update
updateHoststo remove hostnames usingslices.DeleteFuncto avoid index invalidation and missed removals. - Update
(*serviceOptions).makeEnvto safely de-duplicate env vars while preserving “last value wins” semantics and skipping exact repeats. - Add focused tests covering repeated env vars and repeated host entries to prevent regressions.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| cli/command/service/update.go | Switch host removal logic to slices.DeleteFunc to avoid slice-mutation-during-range panics and ensure all duplicates are removed. |
| cli/command/service/update_test.go | Add regression test ensuring --host-rm removes repeated hostnames within a single hosts entry. |
| cli/command/service/opts.go | Rework env de-duplication to avoid unsafe slice mutation during iteration and correctly skip exact duplicates. |
| cli/command/service/opts_test.go | Add TestMakeEnv to cover no-dup, exact-repeat, repeat-then-override, and ordering cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
- What I did
Fixed a panic in
docker service create/docker service updatewhen the same environment variable is passed more than once, and the same bug class inupdateHosts.Both
(*serviceOptions).makeEnvandupdateHostsremoved elements from a slice withs = append(s[:i], s[i+1:]...)while ranging over that same slice. The range expression is evaluated once, so after a removal the loop keeps using the original length: it reads stale elements that shifted down, skips a live element, and can slice past the end of the shrunken slice, which panics.In
makeEnvthere was a second problem feeding the first: thecontinuefor the "no update required" case continued the inner loop instead of skipping the re-append, so an env var passed twice with the same value was stored twice — contradicting the// remove duplicatescomment. A third occurrence with a different value then tried to remove both copies and ran off the end:makeEnvis the first statement ofToService, so this happens before any API call — no daemon is needed to hit it, and the same applies to an--env-filethat lists a variable twice.updateHostshas the same defect when a hostname appears more than once in a single entry:--host-rmeither leaves a copy behind or panics withslice bounds out of range. To be upfront about the impact: the CLI itself does not produce such entries, so reaching that one needs a spec written through the API or swarmkit directly. It is fixed here because it is literally the same bug two files apart, not because I can show it from the CLI.- How I did it
Replaced both hand-rolled removal loops with
slices.DeleteFunc, which removes every match in a single pass and cannot leave a stale index behind. InmakeEnvthe "already present, unchanged" check moved out to the top of the outer loop, which is where the originalcontinuecomment was clearly aiming — so an exact repeat is now skipped instead of appended twice. Behaviour is otherwise unchanged: vars are still processed in order and the last value for a key still wins.I also swept the rest of the tree for the same pattern. The only other in-place removals are
stats.remove(cli/command/container/stats_helpers.go) andListOpts.Delete(opts/opts.go); neither removes inside a range over the slice it mutates, so both are correct and are left alone.- How to verify it
Before this change, on
master:(also reproducible with
printf 'A=1\nA=1\nA=2\n' > /tmp/e && ./dockercli service create --env-file /tmp/e ...)After it, the command proceeds normally and the service gets a single
A=2.makeEnvhad no test coverage at all, so this addsTestMakeEnvcovering the repeat, repeat-then-override, repeat-at-last-position and no-duplicate cases, plusTestUpdateHostsRemoveRepeatedHostfor the--host-rmside. Both fail onmaster(the override case and the host case panic) and pass with the fix:The existing
TestUpdateHosts*tests are untouched and still pass, andgolangci-lint run ./cli/command/service/...is clean.- Human readable description for the release notes
I left the changelog block below empty on purpose:
check-changelogfails a PR that fills it without animpact/label, and labels are maintainer-applied. If you'd like it in the release notes, the line I'd suggest is:Happy to move that into the block once a label is applied.