Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[beta] Backport fix of CVE-2024-24576 #123682

Merged
merged 5 commits into from
Apr 10, 2024

Conversation

pietroalbini
Copy link
Member

@rustbot rustbot added O-windows Operating system: Windows S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-release Relevant to the release subteam, which will review and decide on the PR/issue. labels Apr 9, 2024
@pietroalbini
Copy link
Member Author

@bors r+ p=500 rollup=never

@bors
Copy link
Contributor

bors commented Apr 9, 2024

📌 Commit 4ef17ef has been approved by pietroalbini

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 9, 2024
@bors
Copy link
Contributor

bors commented Apr 9, 2024

⌛ Testing commit 4ef17ef with merge 0f9121c...

bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 9, 2024
@rustbot rustbot added A-testsuite Area: The testsuite used to check the correctness of rustc T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) labels Apr 9, 2024
@pietroalbini
Copy link
Member Author

Backported a fix for the tidy issue we saw in #123681 (comment).

@bors retry r+ p=500 rollup=never

@bors
Copy link
Contributor

bors commented Apr 9, 2024

📌 Commit fa1e080 has been approved by pietroalbini

It is now in the queue for this repository.

@rust-log-analyzer

This comment has been minimized.

bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 9, 2024
@bors
Copy link
Contributor

bors commented Apr 9, 2024

⌛ Testing commit fa1e080 with merge 84cba09...

@rust-log-analyzer
Copy link
Collaborator

A job failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)

@bors
Copy link
Contributor

bors commented Apr 9, 2024

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Apr 9, 2024
@cuviper
Copy link
Member

cuviper commented Apr 9, 2024

@bors retry

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 9, 2024
@bors
Copy link
Contributor

bors commented Apr 9, 2024

⌛ Testing commit fa1e080 with merge 27011d5...

@bors
Copy link
Contributor

bors commented Apr 10, 2024

☀️ Test successful - checks-actions
Approved by: pietroalbini
Pushing 27011d5 to beta...

1 similar comment
@bors
Copy link
Contributor

bors commented Apr 10, 2024

☀️ Test successful - checks-actions
Approved by: pietroalbini
Pushing 27011d5 to beta...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Apr 10, 2024
@bors bors merged commit 27011d5 into rust-lang:beta Apr 10, 2024
12 checks passed
@rustbot rustbot added this to the 1.78.0 milestone Apr 10, 2024
Comment on lines +306 to +307
// Appending an additional double-quote acts as an escape.
cmd.push(b'"' as u16)
Copy link

Choose a reason for hiding this comment

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

This doesn't sound correct to me. A double-quote does leave the state of the quote-flag inside cmd unchanged, but it also adds an extra quote character to the command line being executed, which changes the meaning of the that argument. For example, cmd /c echo " " " should print a single ", as it would do if written ". But if the call was something like echo "contains evil" and the evil would be contained inside those quotes. But after this new transform, the argument looks like it becomes echo """contains evil""" and that evil may be no longer contained inside those quotes. In particular, if "evil" contained an escaped quote such as "foo \" bar", this step appears to now inject an unescaped quote here: """foo \\\"" bar""".

Additionally, \ is not a special character in cmd / batch, so it is suspect to me that would appear in the list of special characters to handle. I think the PR has gotten mixed up on what processing is done by the processing of the arguments to batch, the call within batch, and the program executed by batch. Those each require separate handling (because batch/cmd/windows parsing is a crazy world).

Source: I am the author of the Julia algorithm for handling this (shell_escape_wincmd) https://github.com/JuliaLang/julia/blob/e9a24d4cee4153023a7841a75fc4285229f26720/base/shell.jl#L351-L356, and I once tried to write a blog post about how every algorithm I could find for this online seemed to have a least one significant bug, but I couldn't quite manage to finish listing and validating all of the exception cases that I found: JuliaLang/www.julialang.org@main...vtjnash:www.julialang.org:jn/str-escape#diff-8a99ded732ef1d9e4a4810df7c302c887c5616aa829fa0b52efe57cf7f8e17c8R458-R491

Copy link
Contributor

@tgross35 tgross35 Apr 10, 2024

Choose a reason for hiding this comment

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

Thanks for taking a closer look at this

Cc @ChrisDenton

Copy link
Member

@ChrisDenton ChrisDenton Apr 10, 2024

Choose a reason for hiding this comment

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

echo "contains evil" will become "echo ""contains evil""", which isn't great but is not a shell escape. Similarly "foo \" bar" becomes """foo \\"" bar""".

It's a bit confusing because the code is indeed doing two things. The main goal of course is to prevent shell injection. A secondary goal was to be as close to the old behaviour as possible (so as to not break existing code that worked). The old behaviour didn't really consider cmd.exe at all (though a few escapes were tacked on later) and instead prepared arguments assuming they'd be passed through to an exe. So when there was a decision between accurately preserving them in batch files or preserving them when passed through to an exe, the latter won out.

Copy link

Choose a reason for hiding this comment

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

I see what you mean there, but I still don't think you are using the right algorithm to accomplish that correctly. First of all because there should be double+1 \ here before the " to correctly preserve the " through the Window's command line parser. And secondly because the behavior of doubling the " is undocumented and inconsistent between compilers, so while it usually does preserve a single " it usually also terminates the argument there also:
JuliaLang/www.julialang.org@main...vtjnash:www.julialang.org:jn/str-escape#diff-8a99ded732ef1d9e4a4810df7c302c887c5616aa829fa0b52efe57cf7f8e17c8R409-R411 such that our "contains evil" example results in 2 arguments getting received by C: echo "contains and evil"

Copy link
Member

Choose a reason for hiding this comment

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

We do document that we specifically follow MSVC parsing rules. These rules may not be publicly documented but they have been consistent and well known for well over a decade at this point (e.g. see top voted stackoverflow answers).

Admittedly this is a trade-off. For our normal (not-bat) argument passing we don't make use of "". Maybe we can indeed make better choices here but that's something that can be worked on publicly.

Copy link

Choose a reason for hiding this comment

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

We do document that we specifically follow MSVC parsing rules

I guess that is fair, it seems that this should follow along with those rules. I suppose I should know that, but I forgot that I was the one to write that documentation in ages past: MicrosoftDocs/cpp-docs@e5bdbb7

Copy link

Choose a reason for hiding this comment

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

they have been consistent and well known for well over a decade at this point

They actually changed about 3 years ago (removing was_in_quote). And as the PR noted that the common Windows rules have change several times (although the latest change was probably slightly more than a decade ago):
e26dda5#diff-96f5abd15201d718909ac0227541d568d9985ac9b075a34ae7eb9f5f66a562f9

I have found from experience that most internet sites (particularly stack overflow) have incorrect information regarding Windows and cmd argument parsing (and have attempted to correct some of it), so I put little value in what the top answer said.

Copy link
Member

@ChrisDenton ChrisDenton Apr 11, 2024

Choose a reason for hiding this comment

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

Ok, so in testing it was found that ^" didn't always work to properly escape the quote character in cmd (the double quote was escaped but it could still affect parsing in some situations). The first version of the patch disallowed " for this reason. However, it was found that some users were using double quotes. So our options were:

  • break user's code (which we're very reluctant to do in a point release unless absolutely necessary)
  • have some algorithm for deciding if we can allow quotes (i.e if the quotes are balanced and there's nothing that would be affected by quoting/unquoting)
  • do the "" thing (which not everything supports and will look weird in batch files)

We wanted to avoid the first and the second adds a fair bit complexity (and may be fragile). So it was thought the third was the least worst option.

Copy link

Choose a reason for hiding this comment

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

I don't think ^" ever escapes a quote character: the ^ is just ignored if the subsequent character cannot be quoted, which includes ". The algorithm for 2 is very simple: you just quote everything that might be special except when inside quotes, when you quote nothing. The cmd parser doesn't have a notion of "balanced" quotes, as it simply toggles the escape state at each " scanning left to right, and does not care whether the end state is on or off. This algorithm has the advantage that it doesn't change the actual values of the arguments that the user provided.

Copy link
Member

@ChrisDenton ChrisDenton Apr 11, 2024

Choose a reason for hiding this comment

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

The cmd parser doesn't have a notion of "balanced" quotes, as it simply toggles the escape state at each " scanning left to right, and does not care whether the end state is on or off.

That's what I mean by "balanced", sorry for the imprecision. At the end of a single argument (e.g. %1, etc in bash), we do need to be careful of the escape state,

The algorithm for 2 is very simple: you just quote everything that might be special except when inside quotes, when you quote nothing

That is mostly what we do. But, as far as I'm aware, you can't quote a quote.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-testsuite Area: The testsuite used to check the correctness of rustc merged-by-bors This PR was explicitly merged by bors. O-windows Operating system: Windows S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-release Relevant to the release subteam, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants