[HN Gopher] Breaking Git with a carriage return and cloning RCE
       ___________________________________________________________________
        
       Breaking Git with a carriage return and cloning RCE
        
       Author : dgl
       Score  : 237 points
       Date   : 2025-07-08 17:48 UTC (5 hours ago)
        
 (HTM) web link (dgl.cx)
 (TXT) w3m dump (dgl.cx)
        
       | therealmarv wrote:
       | guess I have to wait a bit more... no update to git 2.50.1 on
       | Homebrew yet.
        
         | leipert wrote:
         | https://github.com/Homebrew/homebrew-core/pull/229423 or brew
         | install git --HEAD
        
       | dwrodri wrote:
       | The year is 2025 and we are still cleaning up string parsing RCEs
       | in C. I should revisit why in the world efforts for a more secure
       | string parsing in C never took off.
        
         | asplake wrote:
         | The article refutes that somewhat:
         | 
         | > I find this particularly interesting because this isn't
         | fundamentally a problem of the software being written in C.
         | These are logic errors that are possible in nearly all
         | languages, the common factor being this is a vulnerability in
         | the interprocess communication of the components (either
         | between git and external processes, or within the components of
         | git itself).
        
         | bpt3 wrote:
         | As mentioned in the article, this is a logic error that has
         | nothing to do with C strings.
        
           | eptcyka wrote:
           | Whilst true, there's a swathe of modern tooling that will
           | aide in marshalling data for IPC. Would you not agree that if
           | protobuf, json or yaml were used, it'd be far less likely for
           | this bug have slipped in?
        
             | alexvitkov wrote:
             | In isolation, for any one particular bug, yes, but if you
             | start applying this logic to everything, even problems as
             | simple as reading some bytes from a file, you end up with a
             | heao of dependencies for the most mundane things. We've
             | tried that, it's bad.
        
               | gpm wrote:
               | On the contrary, we've tried it and it works great.
        
               | sunshowers wrote:
               | No, I think in general you should trust other people with
               | experience in an area more than your own naive self.
               | Division of labor and all that.
               | 
               | There are exceptions, as always, but using dependencies
               | is good as a first approximation.
        
               | eptcyka wrote:
               | I don't believe we must apply any guideline ad absurdum.
               | Using a battle tested marshalling/serialization library
               | is clearly the way to go most often. Of course, one can
               | still construct difficult to parse XML and JSON or any
               | other blob for any given format, but the chances that bad
               | input will result in an RCE are lower.
        
             | bangaladore wrote:
             | The OC was about language choice. You can use protobuf,
             | json or yaml in C as well.
             | 
             | In general, though, all these can be wildly overkill for
             | many tasks. At some point you just need to write good code
             | and actually test it.
        
             | greatgib wrote:
             | Having "safe" yaml parsing is a whole topic of head
             | scratching in whatever language of your choice if you want
             | a rabbit hole to look into...
        
         | jerf wrote:
         | As the article says: "I find this particularly interesting
         | because this isn't fundamentally a problem of the software
         | being written in C. These are logic errors that are possible in
         | nearly all languages, the common factor being this is a
         | vulnerability in the interprocess communication of the
         | components (either between git and external processes, or
         | within the components of git itself). It is possible to draw a
         | parallel with CRLF injection as seen in HTTP (or even SMTP
         | smuggling)."
         | 
         | You can write this in any language. None of them will stop you.
         | I'm on the cutting edge of "stop using C", but this isn't C's
         | fault.
        
           | gpm wrote:
           | You _can_ , but in languages like python/java/go/rust/... you
           | _wouldn 't_, because you wouldn't write serialization/de-
           | serialization code by hand but call out to a battle hardened
           | library.
           | 
           | This vulnerability is the fault of the C ecosystem where
           | there is no reasonable project level package manager so
           | everyone writes everything from scratch. It's exacerbated by
           | the combination of a lack of generics (rust/java's solution),
           | introspection (java/python's solution), and poor preprocessor
           | in C (go's solution) so it wouldn't even be easy to make a
           | ergonomic general purpose parser.
        
             | bangaladore wrote:
             | I have a feeling that this code was developed before any of
             | those languages were widely popular and before their
             | package managers or packages were mature.
             | 
             | This file was written like 20 years ago.
        
               | gpm wrote:
               | Sure, I'm not trying to assign blame to Linus for
               | deciding to write git in C, I'm saying that modern
               | tooling (not C) would prevent the bug with reasonably
               | high probability and that that's a factor when deciding
               | what to do going forwards.
        
             | shakna wrote:
             | Python's pathlib wouldn't help you here, it can encode the
             | necessary bits. Especially with configparser - it's 20 year
             | old configuration reader. Java's story is worse.
             | 
             | What part of this would be prevented by another language?
             | 
             | You'd need to switch your data format to something like
             | json, toml, etc. to prevent this from the outset. But JSON
             | was first standardised 25 years ago, and AJAX wasn't
             | invented when this was written. JSON was a fledgling and
             | not widely used yet.
             | 
             | I guess we had netrc - but that's not standardised and
             | everyone implements it differently. Same story for INI.
             | 
             | There was XML - at a time when it was full of RCEs, and
             | everyone was acknowledging that its parser would be 90% of
             | your program. Would you have joined the people disparaging
             | json at the time as reinventing xml?
             | 
             | This vulnerability is the fault of data formats not being
             | common enough to be widely invented yet.
        
               | gpm wrote:
               | > What part of this would be prevented by another
               | language?
               | 
               | > You'd need to switch your data format to something like
               | json, toml, etc.
               | 
               | The part where if you wrote this in any modern languages
               | ecosystem you would do this.
               | 
               | Yes, modern languages and their ecosystems likely did not
               | exist back then. The lesson going forwards is that we
               | shouldn't keep doing new things like we did back then.
               | 
               | Saying smithing metal by using a pair of hand driven
               | bellows is inefficient isn't to say the blacksmiths ages
               | ago who had no better option were doing something wrong.
        
               | shakna wrote:
               | Ok... So you're not saying C is a problem.
               | 
               | You're saying every few years, we should torch our code
               | and rewrite from scratch, using new tools.
               | 
               | ... Enjoy your collapsing codebase. I'll stick with what
               | works, thanks.
        
               | gpm wrote:
               | What an absurdly bad faith interpretation. I never said
               | anything to even suggest abandoning old code.
               | 
               | As demonstrated by vulnerabilities like the one in the
               | article, _C (and its ecosystem) doesn 't "work"_, so I'm
               | glad to hear that you won't be sticking with that for new
               | projects going forwards.
        
         | mrkeen wrote:
         | C programmers don't see C problems. They see logic errors that
         | are possible in any language.
        
           | dietr1ch wrote:
           | Running with scissors isn't a problem. The problem is
           | stabbing yourself with them. Isn't it obvious?
        
         | alexvitkov wrote:
         | We keep getting RCEs in C because tons of useful programs are
         | written in C. If someone writes a useful program in Rust, we'll
         | get RCEs in Rust.
        
           | dietr1ch wrote:
           | It's not that only C programs are useful. It's that subtle
           | mistakes on C result in more catastrophic vulnerabilities.
           | 
           | Make a mistake in application code in a language like, say
           | Java, and you'll end up with an exception.
        
           | umanwizard wrote:
           | There are a lot of useful programs written in Rust nowadays.
           | This comment might have made more sense like 5 years ago.
        
         | smileson2 wrote:
         | If only it were just the c code that was causing people to be
         | owned lol
        
         | tuetuopay wrote:
         | Using other languages would likely fix the issue but as a side-
         | effect. Most people would expect a C-vs-Rust comparison so I'll
         | take Go as an example.
         | 
         | Nobody would write the configuration parsing code by hand, and
         | just use whatever TOML library available at hand for Go. No INI
         | shenanigans, and people would just use any available stricter
         | format (strings _must_ be quoted in TOML).
         | 
         | So yeah, Rust and Go and Python and Java and Node and Ruby and
         | whatnot would not have the bug just by virtue of having a
         | package manager. The actual language is irrelevant.
         | 
         | However, whatever the language, the same hand implementation
         | would have had the exact same bug.
        
       | deanc wrote:
       | Would homebrew itself be problematic here? Does it do recursive
       | cloning?
       | 
       | At least a cursory glance at the repo suggests it might:
       | https://github.com/Homebrew/brew/blob/700d67a85e0129ab8a893f...
        
         | msgodel wrote:
         | It would be odd if it didn't. Although the goal of homebrew is
         | to execute the code in the repo.
         | 
         | The only situation where the RCE here is a problem is if you
         | clone github repos containing data you don't want to execute.
         | That's fairly unusual.
        
           | leni536 wrote:
           | The question is whether recursive submodule checkout happens
           | after some integrity/signature validation or before. The RCE
           | can be an issue in the latter case.
        
       | armchairhacker wrote:
       | > The result of all this, is when a submodule clone is performed,
       | it might read one location from path = ..., but write out a
       | different path that doesn't end with the ^M.
       | 
       | How does this achieve "remote code execution" as the article
       | states? How serious is it from a security perspective?
       | 
       | > I'm not sharing a PoC yet, but it is an almost trivial
       | modification of an exploit for CVE-2024-32002. There is also a
       | test in the commit fixing it that should give large hints.
       | 
       | EDIT: from the CVE-2024-32002
       | 
       | > Repositories with submodules can be crafted in a way that
       | exploits a bug in Git whereby it can be fooled into writing files
       | not into the submodule's worktree but into a .git/ directory.
       | This allows writing a hook that will be executed while the clone
       | operation is still running, giving the user no opportunity to
       | inspect the code that is being executed.
       | 
       | So a repository can contain a malicious git hook. Normally git
       | hooks aren't installed by 'git clone', but this exploit allows
       | one to, and a git hook can run during the clone operation.
        
         | beala wrote:
         | More information here (https://github.blog/open-source/git/git-
         | security-vulnerabili...) on the new CVE:
         | 
         | > When reading a configuration value, Git will strip any
         | trailing carriage return (CR) and line feed (LF) characters.
         | When writing a configuration value, however, Git does not quote
         | trailing CR characters, causing them to be lost when they are
         | read later on. When initializing a submodule whose path
         | contains a trailing CR character, the stripped path is used,
         | causing the submodule to be checked out in the wrong place.
         | 
         | > If a symlink already exists between the stripped path and the
         | submodule's hooks directory, an attacker can execute arbitrary
         | code through the submodule's post-checkout hook.
         | 
         | Along with a bunch of other git CVEs that are worth a look.
        
           | gitfan86 wrote:
           | This seems easy for GitHub to block
        
         | caust1c wrote:
         | Yes, unfortunately it's pretty trivial. Any time arbitrary file
         | write is possible, RCE is usually possible too.
        
       | 10000truths wrote:
       | This is a big problem with using ad-hoc DSLs for config - there's
       | often no formal specification for the grammar, and so the source
       | of truth for parsing is spread between the home-grown
       | serialization implementation and the home-grown deserialization
       | implementation. If they get out of sync (e.g. someone adds new
       | grammar to the parser but forgets to update the writer), you end
       | up with a parser differential, and tick goes the time bomb. The
       | lesson: _have one source of truth_ , and generate everything that
       | relies on it from that.
        
         | ajross wrote:
         | Nitpick: the DSL here ("ini file format") is arguably ad-hoc,
         | but it's extremely common and well-understood, and simple
         | enough to make a common law specification work well enough in
         | practice. The bug here wasn't due to the format. What you're
         | actually complaining about is the hand-coded parser[1] sitting
         | in the middle of a C program like a bomb waiting to go off.
         | And, yes, that nonsense should have died decades ago.
         | 
         | There are places for clever hand code, even in C, even in the
         | modern world. Data interchange is _very not much not one of
         | them_. Just don 't do this. If you want .ini, use toml. Use
         | JSON if you don't. Even YAML is OK. Those with a penchant for
         | pain like XML. And if you have convinced yourself your format
         | must be binary (you're wrong, it doesn't), protobufs are there
         | for you.
         | 
         | But absolutely, positively, never write a parser unless your
         | job title is "programming language author". Use a library for
         | this, even if you don't use libraries for anything else.
         | 
         | [1] Fine fine, lexer. We are nitpicking, after all.
        
           | heisenbit wrote:
           | How many hand crafted lexers dealing with lf vs. cr-lf
           | encodings do exist? My guess is n > ( number of people who
           | coded > 10 KSLOC ).
        
         | fpoling wrote:
         | This bug is orthogonal to one source of truth. It is a pure
         | logic bug that could have existed in a standard system library
         | for config files if such existed on Unix.
         | 
         | And consider that consequences of such bug would be much worse
         | if it was in a standard system library. At least here it is
         | limited mostly to developers where machines are updated.
        
       | b0a04gl wrote:
       | why tf is git still running submodule hooks during clone at all.
       | like think. youre cloning a repo which you didnt write it or
       | audit it. and git just... runs a post checkout hook from a
       | submodule it just fetched off the internet. even with this CRLF
       | bug fixed, thats still bananas
        
       | HappMacDonald wrote:
       | I completely disagree with author's (oft quoted here in comments)
       | statement:
       | 
       | > I find this particularly interesting because this isn't
       | fundamentally a problem of the software being written in C. These
       | are logic errors that are possible in nearly all languages
       | 
       | For Christ's sake, Turing taught us that any error in one
       | language is _possible_ in any other language. You can even get
       | double free in Rust if you take the time to build an entire
       | machine emulator and then run something that uses Malloc in the
       | ensuing VM. Rust and similar memory safe languages can _emulate_
       | literally any problem C can make a mine field out of.. but logic
       | errors being  "possible" to perform are significantly different
       | from logic errors being the first tool available to pull out of
       | one's toolbox.
       | 
       | Other comments have cited that in non-C languages a person would
       | be more likely to reach for a security-hardened library first,
       | which I agree might be helpful.. but replies to those comments
       | also correctly point out that this trades one problem for another
       | with dependency hell, and I would add on top of that the issue
       | that a widely relied upon library can also increase the surface
       | area of attack _when_ a novel exploit gets found in it. Libraries
       | can be a very powerful tool but neither are they a panacea.
       | 
       | I would argue that the real value in a more data-safe language
       | (be that Rust or Haskell or LISP et al) is in offering the
       | _built-in_ abstractions which lend themselves to more carefully
       | modeling data than as a firehose of octets which a person then
       | assumes they need to state-switch over like some kind of raw
       | Turing machine.
       | 
       | "Parse, don't validate" is a lot easier to stick to when you're
       | coding in a language designed with a precept like that in mind vs
       | a language designed to be only slightly more abstract than
       | machine code where one can merely be grateful that they aren't
       | forced to use jump instructions for every control flow action.
        
         | lilyball wrote:
         | I can easily see this bug happening in Rust. At some level you
         | need to transform your data model into text to write out, and
         | to parse incoming text. If you want to parse linewise you might
         | use BufRead::lines(), and then write a parser for those lines.
         | That parser won't touch CRs at all, which means when you do the
         | opposite and write the code that serializes your data model
         | back to lines, it's easy to forget that you need to avoid
         | having a trailing CR, since CR appears nowhere else in your
         | code.
        
           | HappMacDonald wrote:
           | Well the question then becomes "how do you identify the
           | quoting that needs to happen on the line" and tactics common
           | in Rust enabled by features available in Rust will still lead
           | a person away from this pattern of error.
           | 
           | One tool I'd have probably reached for (long before having
           | heard of this particular corner case to avoid) would have
           | been whitespace trimming, and CR counts as whitespace. Plus
           | folk outside of C are also more likely to aim a regex at a
           | line they want to parse, and anyone who's been writing regex
           | for more than 5 minutes gets into the habit of adding `\s*`
           | adjacent to beginning of line and end of line markers (and
           | outside of capture groups) which in this case achieves the
           | same end.
        
             | wizzwizz4 wrote:
             | I've been writing regular expressions for at least 8 years,
             | and I'm not sure I've _ever_ written `\s*`.
        
           | tetha wrote:
           | And - having dealt with parser construction in university for
           | a few months - the only real way to deal with this is fuzzing
           | and round trip tests.
           | 
           | It sounds defeatist, but non-trivial parsers end up with a
           | huge state space very quickly - and entirely strange error
           | situations and problematic inputs. And "non-trivial" starts a
           | lot sooner than one would assume. As the article shows, even
           | "one element per line" ends up non-trivial once you support
           | two platforms. "foo\r\n" could be tokenized/parsed in 3 or
           | even 4 different ways or so.
           | 
           | It just becomes worse from there. And then Unicode happened.
        
         | SpaceNugget wrote:
         | > You can even get double free in Rust if you take the time to
         | build an entire machine emulator and then run something that
         | uses Malloc in the ensuing VM. Rust and similar memory safe
         | languages can emulate literally any problem C can make a mine
         | field out of..
         | 
         | That doesn't have any relevance to a discussion about memory
         | safety in C vs rust. Invalid memory access in the emulated
         | machine won't be able to access memory from other processes on
         | the host system. Two languages being turing complete does not
         | make them the same language. And it definitely does not make
         | them bug for bug compatible. Rust _really_ does enable you to
         | write memory safe programs.
        
           | prmph wrote:
           | Sounds like you actually agree with the comment you are
           | replying to.
        
         | markasoftware wrote:
         | As you point out, the most serious way to undermine the
         | "safety" features in a "safe" language like Rust is to
         | implement a VM, programming language, serdes framework, etc,
         | because these operate outside of Rust's type system and memory
         | safety.
         | 
         | And that's exactly what the Git developers did here: They made
         | an in-house configuration file format. If implemented in Rust,
         | it would bypass most of Rust's safety features, particularly,
         | type-safety.
        
           | nixosbestos wrote:
           | It is mind-blowing the things people come up with when it
           | comes to Rust vs C conversations. The same colvoluted crap
           | for _years_ at this point.
           | 
           | No, just no. I'm sorry, Ive implemented countless custom
           | formats in Rust and have NEVER had to side step safe/unsafe
           | or otherwise sacrifice type safety. Just what an absurd
           | claim.
           | 
           | Maybe for some binary (de)serialization you get fancy (lol
           | and are still likely to be far better off than with C) but
           | goodness, I cannot imagine a single reason why a config file
           | parser would need to be (type)-unsafe.
        
             | sophacles wrote:
             | The person you replied to didn't say that you had to bypass
             | safe. This bug is orthogonal to type and memory safety, its
             | a different issue.
             | 
             | The git bug in question could be written in 100% safe rust
             | using as much or as little of the type system[1] as you
             | want. It's a logic error when parsing a string.
             | 
             | I dev rust full-time, and I've spent a lot of time writing
             | protocol parsers. It's easy to forget to check this or that
             | byte/string for every possible edge case as you're parsing
             | it into some rust type, and happens all the time in rust,
             | just like it did in C or python or go when I used those
             | languages. This bug (if anything) is the type of thing that
             | is solved with good tokenizer design and testing, and using
             | more small, independently tested functions - again not at
             | all related to the type system.
             | 
             | [1] Although in rust you can arrange your types so that
             | this sort of bug is harder to implement or easier to catch
             | than in most languages... but doing that requires an up-
             | front understanding that logic bugs are just as possible in
             | rust as in other languages, as well as some experience to
             | avoid awkwardness when setting the types up.
        
               | the8472 wrote:
               | [delayed]
        
         | umanwizard wrote:
         | > You can even get double free in Rust if you take the time to
         | build an entire machine emulator and then run something that
         | uses Malloc in the ensuing VM.
         | 
         | No, this wouldn't be a double free in Rust, it'd be a double
         | free in whatever language you used to write the emulated code.
         | 
         | The distinction is meaningful, because the logic error he's
         | talking about _is_ possible in _actual_ rust (even without
         | unsafe), not just theoretically in some virtual system that you
         | can use Rust to write a simulation for.
        
       | Lockal wrote:
       | "trivial modification of an existing exploit"...
       | 
       | Why git does not use Landlock? I know it is Linux-only, but why?
       | "git clone" should only have r/o access to config directory and
       | r/w to clone directory. And no subprocesses. In every exploit
       | demo: "Yep, <s>it goes to a square hole</s> it launches a
       | calculator".
        
         | TheDong wrote:
         | > no subprocesses
         | 
         | I guess you're okay with breaking all git hooks, including
         | post-checkout, because those are subprocesses as a feature.
         | 
         | You can always run your git operations in a container with
         | seccomp or such if you're not using any of the many features
         | that it breaks
        
           | Spivak wrote:
           | This would also break custom commands. Which if you don't
           | know about it, is a pretty cool feature.
           | 
           | Drop a git-something executable in your path and you can
           | execute it as git something.
        
             | byearthithatius wrote:
             | Why is this helpful? Just add the executable itself to path
             | and execute it with "something" instead of "git something".
             | Why are we making git an intermediary ? I am kind of stupid
             | and this is genuine.
        
               | wbl wrote:
               | Because something might make less sense on its own or
               | conflict with another tool.
        
               | mkesper wrote:
               | Because it's thematically a part of a git workflow.
        
               | joseda-hg wrote:
               | Because if it's part of the repo, you don't depend on the
               | host to take the extra step, which, if you're working
               | from ephemeral instances or places where that step would
               | have to be repeated, is a god send
        
               | pirates wrote:
               | Because the joke doesn't land if typing "git gud" doesn't
               | actually do something.
               | 
               | To your point, I would say that it's "easy" rather than
               | strictly helpful. There is a plugin I maintain internally
               | that can be invoked by calling "helm <thing>" if I go
               | through the necessary steps to have it installable by the
               | helm plugin command. Under the hood it's just a small
               | binary that you can put in your $PATH and it'll work
               | fine, but there are tons of developers and PMs and other
               | people at the company who don't know what a path variable
               | is, or how to set it, or what a terminal is, or what
               | shell they're running, or who know that they can do "helm
               | X" and "helm Y", so why not "helm Z" for my plugin, etc
               | ... It would be a hell of a lot easier to just ship the
               | raw executable, but to those people and execs and mangers
               | and stuff, it looks good if I can show it off next to the
               | native stuff.
               | 
               | Whenever I have to help users with it, I notice that
               | nearly everyone uses it with helm and not calling by the
               | binary directly. It just comes down to the fact that
               | presentation and perceived ease of use counts for a lot
               | when people evaluate whether they want to make a tool
               | part of their workflow.
        
               | Izkata wrote:
               | It allows things to be added and removed from the main
               | executable without changing the interface. This means if
               | someone has a good idea everyone starts using, and they
               | implemented it as a subcommand like this, it could
               | eventually be integrated into git without anyone having
               | to migrate. Also all the subcommands are implemented as
               | separate executables like this anyway.
               | 
               | For example in /usr/lib/git-core/ with git 2.25.1 on
               | Ubuntu, "git-rebase" is a symlink to "git". But on an
               | older Centos VM I have access to, in /usr/libexec/git-
               | core/ with git 2.16.5, "git-rebase" is a separate shell
               | script.
        
               | sophacles wrote:
               | Git itself uses this functionality. On my ubuntu system
               | the path is `/usr/lib/git-core/` and in it you see all
               | sorts of bins for "git commands", e.g `git-rm`, `git-mv`,
               | `git-difftool`, etc. A lot of these are just links back
               | to the git binary these days, but many features begin
               | life as a standalone `git-$X` executable, and back in
               | early git days much more functionality was split across
               | executables. (The ones that are now links back to git are
               | largely for scripting purposes, a lot of git "plugins"
               | and various CI type scripts will call `git-mv` rather
               | than trying to get quoting right around calling `git mv`
               | for example.
               | 
               | It also helps make plugins easier to distribute. I don't
               | want to have to type `git-x` sometimes and `git y`
               | others, and if I want my plugin to get adoption, I really
               | really don't want that. So things like git-lfs, git-
               | annex, etc can easily be distributed, documented as a
               | plugin, and generally be considered as "a part of git",
               | rather than a separate command.
               | 
               | This pattern is also not unique to git. Other softwares
               | have followed it, notably cargo.
        
         | SSLy wrote:
         | > And no subprocesses.
         | 
         | have you never used git over ssh?
        
       | smaudet wrote:
       | Is it just me or is the font an eyestrain on this blog?
        
         | MisterTea wrote:
         | I am not sure if there's bias on my part after reading your
         | comment but yes, it is bothersome.
        
         | metalliqaz wrote:
         | yeah I see what you mean. it's like the anti-aliasing is broken
        
       | JdeBP wrote:
       | Reading someone quote Jon Postel in the context of CR+LF brings
       | back memories.
       | 
       | * https://jdebp.uk/FGA/qmail-myths-dispelled.html#MythAboutBar...
       | 
       | "that may not be the most sensible advice now", says M.
       | Leadbeater today. We were saying that a lot more unequivocally,
       | back in 2003. (-:
       | 
       | As Mark Crispin said then, the interpretations that people put on
       | it are not what M. Postel would have agreed with.
       | 
       | Back in the late 1990s, Daniel J. Bernstein did the famous
       | analysis that noted that parsing and quoting when converting
       | between human-readable and machine-readable is a source of
       | problems. And here we are, over a quarter of a century later,
       | with a quoter that doesn't quote CRs (and even _after_ the fix
       | does not look for _all_ whitespace characters).
       | 
       | Amusingly, git blame says that the offending code was written 19
       | years ago, around the time that Daniel J. Bernstein was doing the
       | 10 year retrospective on the dicta about parsing and quoting.
       | 
       | * https://github.com/git/git/commit/cdd4fb15cf06ec1de588bee457...
       | 
       | * https://cr.yp.to/qmail/qmailsec-20071101.pdf
       | 
       | I suppose that we just have to keep repeating the lessons that
       | were already hard learned in the 20th century, and still apply in
       | the 21st.
        
       | lossolo wrote:
       | It seems like Homebrew still provides a vulnerable version, the
       | same goes for Debian Bookworm.
        
       | dwheeler wrote:
       | Ah yes, yet ANOTHER vulnerability caused because Linux and most
       | Unixes allow control characters in filenames. This ability's
       | primary purpose appears to be to enable attacks and to make it
       | significantly more difficult to write correct code. For example,
       | you're not supposed to exchange filenames a line at a time, since
       | filenames can contain newlines.
       | 
       | See my discussion here: https://dwheeler.com/essays/fixing-unix-
       | linux-filenames.html
       | 
       | One piece of good news: POSIX recently added xargs -0 and find
       | -print0, making it a little easier to portably handle such
       | filenames. Still, it's a pain.
       | 
       | I plan to complete my "safename" Linux module I started years
       | ago. When enabled, it prevents creating filenames in certain
       | cases such as those with control characters. It won't prevent all
       | problems, but it's a decent hardening mechanism that prevents
       | problems in many cases.
        
         | layer8 wrote:
         | You can get similar vulnerabilities with Unicode normalization,
         | with mismatched code pages/character encodings, or, as the
         | article points out, with a case-insensitive file system. That's
         | not to say that control characters should be allowed in file
         | names, but there's an inherent risk whenever byte sequences are
         | being decoded or normalized into something else.
        
       | IshKebab wrote:
       | Zero surprise there's a bug in git's quoting. That code is
       | mental.
        
       | acheong08 wrote:
       | Reproduced the issue after a bit:
       | https://github.com/acheong08/CVE-2025-48384 Then immediately went
       | to update my git version. Still not up on Arch yet. Will refrain
       | from pulling anything but I bet it'll take quite a while for most
       | people to upgrade. Putting it in any reasonable popular repo
       | where there are perhaps automated pulls will be interesting.
        
         | orblivion wrote:
         | So this was disclosed before patching? With all of the alarming
         | "here's how we can pwn your machine" posts turning out to be
         | months after the fact, I figured by now that these blog posts
         | all happen after all the distros have long patched it.
         | 
         | It seems like it would be appropriate to make it clear "this is
         | important now" vs "don't worry you probably already patched
         | this" in the headline to save our time for those who aren't
         | just reading these posts out of interest.
        
           | acheong08 wrote:
           | Commits fixing the bug date back around 3 or 4 weeks. The
           | patched release came 3 weeks ago. Perhaps some parties
           | weren't informed that it's security critical (Homebrew, Arch,
           | etc) and are now scrambling
        
       ___________________________________________________________________
       (page generated 2025-07-08 23:00 UTC)