4

Stenberg: DISPUTED, not REJECTED

 6 months ago
source link: https://lwn.net/Articles/963240/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Stenberg: DISPUTED, not REJECTED

[Posted February 23, 2024 by daroc]

The Curl project has previously had problems with CVEs issued for things that are not security issues. On February 21, Daniel Stenberg wrote about the Curl project's most recent issue with the CVE system, saying:

I keep insisting that the CVE system is broken and that the database of existing CVEs hosted by MITRE (and imported into lots of other databases) is full of questionable content and plenty of downright lies. A primary explanation for us being in this ugly situation is that it is simply next to impossible to get rid of invalid CVEs.

(Log in to post comments)

Undefined Behaviour as usual

Posted Feb 23, 2024 16:44 UTC (Fri) by tialaramex (subscriber, #21167) [Link]

This "not a vulnerability" CVE-2020-1909 is a signed overflow. I'm sure Daniel believes it's harmless. I'm sure his (internal) initial reporter believes it's harmless. In reality it might be harmless on my system and yours. We have no way to know, it's completely impractical to figure it out across the broad swathe of supported platforms and deployed uses of Curl. So actually "it's not a vulnerability" just comes down to Daniel's gut feeling. But in future Daniel's gut feeling decides whether a CVE is issued.

C says if we do signed arithmetic and there's an overflow, that's Undefined Behaviour. The compiler can transform the program in any way it sees fit so long as it preserves observable behaviour - all transforms of UB are valid, so the behaviour of the software in this case is in reality completely arbitrary. This "not a vulnerability" could cause absolutely anything to happen.

I have absolutely no doubt that this "not a vulnerability" distinction is not useful, and I suspect it's actively harmful. If your program has documented behaviour but it doesn't actually do what was documented it's not useful to pretend to know whether this deviation is or is not a "vulnerability". My guess is that in insisting that some cases are and some are not, you help bad guys circle the pieces of your system you're not actually defending...

I understand Daniel's instinct. But it's misdirected here. If the worst bug you had in 2020 was an integer overflow in command line parsing code that's not a bad year, take the win. Unfortunately it wasn't the worst Curl bug in 2020.

For whatever it's worth, this variable should have a Duration type but neither C nor its standard library provide one. And so everything flows from there. Why are we multiplying it by 1000? Because we have a retry wait in seconds but we want to calculate milliseconds. In C instead it's a signed "long" integer and of course those have Undefined Behaviour on overflow.

In the more recent CVE that Daniel mentions it's a different cause of Undefined Behaviour. In this case a classic one-past-the-end pointer dereference. But again, Daniel characterises it as fine, because in his mind the worst case is a crash, even though "Undefined Behaviour" is not in fact specified as "Don't worry, it's fine, worst case it will crash"...

Mostly the (presumably unintended) effect of this article was to make me think Curl is worse software and the people maintaining it have prioritised their personal feeling of self-worth and aren't too bothered whether the software is good.

Undefined Behaviour as usual

Posted Feb 23, 2024 16:58 UTC (Fri) by JoeBuck (subscriber, #2330) [Link]

Everything you say here is correct in that the bugs should be fixed. However, we live in a world where people are encouraged by many software distributors to do

curl https:// some.domain / who_knows_what_this_is.sh | sh

and sometimes they are told to become root first. Compared to the possible negative consequences of that, other security issues with Curl are in the noise.

Undefined Behaviour as usual

Posted Feb 23, 2024 17:19 UTC (Fri) by wtarreau (subscriber, #51152) [Link]

Yeah and it doesn't take long to even find examples of "curl -k ... | sh", that users sometimes fail to use because they place sudo on curl not bash, until being suggested to do so... I totally agree that this *this* is a real problem and it's a cultural problem, not easy to fix via just filing a CVE.

Undefined Behaviour as usual

Posted Feb 23, 2024 17:02 UTC (Fri) by wtarreau (subscriber, #51152) [Link]

This is exactly the type of generic analysis that focuses on pure theory and neither on code nor use cases that made CVEs totally useless over the years and harms software development and security in general by making reports not trustable anymore. Everyone believes they found the bug that could have caused the world to collapse and wants to be rewarded for saving it in time, filing a CVE with the highest possible CVSS. But practical implications are no longer analyzed.

Why don't you trust the analysis the developer and maintainer of the project did on the impacts of these issues ?

Undefined Behaviour as usual

Posted Feb 23, 2024 17:35 UTC (Fri) by mb (subscriber, #50428) [Link]

> Why don't you trust the analysis the developer and maintainer of the project did on the impacts of these issues ?

The code does not seem to be compiled with -fwrapv. (I can't find it in the sources)
Therefore, signed overflow is UB in curl.

One cannot rule out security implications without knowing the whole system in that case.
For a given set of compiler, libs, curl, etc.. maybe one can assess that it is a harmless bug by looking at the generated asm code.
But if I use a different compiler version, the assessment is completely invalid. It could very well have security implications in my setup.

Curl should use -fwrapv or -ftrapv.

Undefined Behaviour as usual

Posted Feb 23, 2024 18:12 UTC (Fri) by Sesse (subscriber, #53779) [Link]

Can you point to any (remotely real) system where this actually causes a security issue?

Undefined Behaviour as usual

Posted Feb 23, 2024 18:15 UTC (Fri) by mb (subscriber, #50428) [Link]

No. Because I have not tried to find one.

But neither can curl ensure there is no such system anywhere.

Undefined Behaviour as usual

Posted Feb 23, 2024 18:25 UTC (Fri) by Sesse (subscriber, #53779) [Link]

Well, normally, the burden of proof is on the accuser? I mean, I'm pretty sure curl will hit UB in various ways if you have e.g. int at 18 bits (presumably with 9-bit bytes), which is also not impossible at all as per the C standard. Would you file curl CVEs for every code that would behave incorrectly on such a system? After all, curl certainly cannot rule out that such a system exists somewhere. Since you seem to assume -fwrapv would nullify this bug (even though it does absolutely nothing to change the C standard), what about compilers where such an option does not exist?

Sometimes, you can do bad things (have bugs) without them turning into a security vulnerability in practice.

Undefined Behaviour as usual

Posted Feb 23, 2024 18:35 UTC (Fri) by mb (subscriber, #50428) [Link]

>Well, normally, the burden of proof is on the accuser?

No. That is not how security works.

> if you have e.g. int at 18 bits (presumably with 9-bit bytes),

What-about-ism.
Has nothing to do with signed-overflow being UB.

>Since you seem to assume -fwrapv would nullify this bug

No. I didn't say that. I was talking about UB. Not whether the bug would still be a bug or not.

Undefined Behaviour as usual

Posted Feb 23, 2024 18:41 UTC (Fri) by wtarreau (subscriber, #51152) [Link]

What's fun is that often the people who go through extreme mind-stretching to imagine a possible vulnerability in a random bug are the same who complain about the excess of CVEs reported in new stable kernels, which just apply a softened version of their logic :-)

Undefined Behaviour as usual

Posted Feb 24, 2024 0:19 UTC (Sat) by mb (subscriber, #50428) [Link]

Do you have an actual example of such a person?
I could not imagine a single one.

Undefined Behaviour as usual

Posted Feb 23, 2024 21:57 UTC (Fri) by bagder (guest, #38414) [Link]

No they can't. This, as many other of these UB problems are mostly *theoretical* problems in imaginary systems that *might* "do anything". Blindly considering all UBs to be security problems is not helping the world.

I consider it a fairly important property that a security problem should be possible to actually happen on existing hardware where the code was built with existing compilers. If not, it actually is not a security problem and we don't do any service to the world by treating as such.

Undefined Behaviour as usual

Posted Feb 23, 2024 22:08 UTC (Fri) by mb (subscriber, #50428) [Link]

>we don't do any service to the world by treating as such.

That means you'll have to constantly re-assess old UB bugs with every new compiler and system release.
That's an almost infinite amount of work.
Treating UB bugs as security bugs and fixing them right away is easy compared to that.

Undefined Behaviour as usual

Posted Feb 24, 2024 1:03 UTC (Sat) by jwarnica (subscriber, #27492) [Link]

We already have to reassess all behavior, even defined behavior, of new compiler and system releases, as the compiler and system itself may be buggy. Or less buggy, but previously relied upon buggy behavior changed in usually good, but one particular situation, bad ways.

Undefined Behaviour as usual

Posted Feb 24, 2024 5:57 UTC (Sat) by jmspeex (subscriber, #51639) [Link]

> One cannot rule out security implications without knowing the whole system in that case.

Correct. But then that is also true of most bugs you find in software. Based on that alone almost all bugs should be filed at the highest severity, which isn't exactly helpful. Any UB should get fixed, but when UB gets discovered in a piece of code, someone has to make a reasonable effort to figure out how bad the impact is likely to be. Write out of bounds on the stack: very very bad. Integer wrap-around in a loop counter: could be bad unless careful analysis shows it's unlikely to be. Integer overflow in the value that only gets printed to a log: much less likely to be exploited unless proven otherwise.

Undefined Behaviour as usual

Posted Feb 23, 2024 17:34 UTC (Fri) by flussence (subscriber, #85566) [Link]

> C says if we do signed arithmetic and there's an overflow, that's Undefined Behaviour. The compiler can transform the program in any way it sees fit so long as it preserves observable behaviour - all transforms of UB are valid, so the behaviour of the software in this case is in reality completely arbitrary.

This describes a fantasy world where C compilers (and perhaps all software) are made by insane villains and actively abuse people for doing things outside what a written standard specifies, and to be blunt, it's just "free speech" advocacy with different inflection. I for one am glad the tech culture of 40 years ago has been largely stomped out by more reasonable people.

Undefined Behaviour as usual

Posted Feb 23, 2024 17:37 UTC (Fri) by mb (subscriber, #50428) [Link]

> are made by insane villains and actively abuse people

Compilers exploiting UB happens all the time. It is the base of all optimizations.

> for doing things outside what a written standard specifies,

UB is by the very definition of UB outside of what the standard specifies.

Undefined Behaviour as usual

Posted Feb 23, 2024 18:44 UTC (Fri) by fw (subscriber, #26023) [Link]

Compilers exploiting UB happens all the time. It is the base of all optimizations.

Not really, Java and C# do not have undefined behavior, and yet there are optimizing compilers.

Even for C, it's a rather extreme position to say that register allocation (probably among the top three optimizations to implement in a compiler for current architectures) depends on undefined behavior. For others like constant propagation it's a bit of a stretch, too.

Undefined Behaviour as usual

Posted Feb 23, 2024 22:18 UTC (Fri) by khim (subscriber, #9252) [Link]

> Not really, Java and C# do not have undefined behavior, and yet there are optimizing compilers.

Java and C# absolutely do have undefined behavior. It's just handled like Rust handles it: “safe” language guarantees absence of UB by compiler whole “unsafe” part allows on to write programs with UB.

Java forces you to write these parts in entirely different language using JNI while C# have an unsafe subset, similarly to Rust, but in both cases UB is still, very much, form the basis for all optimizations.

> Even for C, it's a rather extreme position to say that register allocation (probably among the top three optimizations to implement in a compiler for current architectures) depends on undefined behavior.

Of course it does. Everything in C depends on absence of undefined behavior. Simply because it's permitted to convert pointer to function into pointer to chat *. This may break your program if it does register allocation in a different way.

And it's not even theoretical issue! Back in MS-DOS era register variables could only use si and di (and they weren't used for anything else) and thus it was possible to write code that would [ab]use that in it's signal handler.

> For others like constant propagation it's a bit of a stretch, too.

That one may be exploiting by finding and changing constants in the compiler code. And that, too, was used back when compilers weren't smart enough to break such tricks.

Undefined Behaviour as usual

Posted Feb 23, 2024 19:34 UTC (Fri) by geofft (subscriber, #59789) [Link]

I think the traditional description of undefined behavior as "demons fly out of your nose" has done a disservice to understanding it. (After all, if the generated code had access to either demons or your nose, there would be a security vulnerability somewhere in granting that power to your userspace account in the first place. :) )

The point of undefined behavior is not that the compiler is allowed to be lawful-evil about how it interpreters your code, and so you have to be paranoid about what it might do. The point is that an optimizing compiler is permitted to assume that you are writing reasonable code that does not require the compiler to be paranoid about what you meant, and so it can make reasonable optimizations on real-world code. And every compiler that people actually use is optimizing. (There is a loose conceptual connection here with speculative execution vulnerabilities: you can avoid them with a non-speculating CPU, but nobody seems to be buying those.)

The code behind CVE-2023-52071 is actually a pretty good example of this:

WCHAR prefix[3] = {0};
if (something) {
    DEBUGASSERT(prefix[3] == L'\0');
    more stuff;
}
While it's pretty obvious to a human reader in context that this is just a typo, it's probably harder for a compiler to distinguish this from, say,
#ifdef LINUX
#define NUMBER_OF_THINGS 10
#else
#define NUMBER_OF_THINGS 5
#endif

thing_t things[NUMBER_OF_THINGS] = {0};
if (some_function_in_another_file_that_only_succeeds_on_linux()) {
    thing[7] = something;
    more stuff;
}
which, as long as some_function_in_another_file_that_only_succeeds_on_linux() does what it says, never actually invokes undefined behavior. The compiler can notice that the assignment is undefined in the non-Linux case, and instead of doing something villainous, it can do something useful, i.e., assume that the assignment statement cannot be reached and dead-code-eliminate it and everything after it until the closing brace - and then dead-code eliminate the function call because there's nothing left.

However, in the actual case in the curl source code, the dead-code elimination is actually pretty bad! You do really want that code to execute; the coder's intention was not that the block was skippable. The compiler can do the exact same "useful" action and get you a pretty negative result: the curl command does no output (I think), but it's returning success anyway. It's not far-fetched to imagine that in turn leading to unexpected data loss. The compiler does not need to be actively evil to cause a real problem.

(Note that what's happening isn't that the compiler is doing something in response to undefined behavior being invoked. The compiler is simply not doing something on the assumption that undefined behavior is never invoked; specifically, it just doesn't compile the block. No real-world compiler has any interest in inserting code to do something weird that it wouldn't otherwise insert. But even so, optimizing out something that shouldn't have been optimized can cause problems - impact not intent and all that.)

Signed overflow being undefined behavior is a little bit silly because a well-intentioned optimizing compiler will only use that optimization for one purpose: to emit whatever the underlying CPU's most efficient arithmetic instructions are to handle the non-overflowing case. On essentially every modern CPU, that's two's-complement wrapping operations, but the historic existence of other CPUs means that the standard wanted to allow optimizing compilers to have a chance on those platforms too. Today it would be reasonable to make it no longer undefined behavior. All the other types of undefined behavior are undefined because there are reasonable optimizations that users actually want their compilers to do. Strict aliasing means that a loop that reads an array behind a pointer doesn't have to reread the pointer each time through, just in case something else in the loop changed it. Data races are undefined so that compilers don't have to use atomic operations or lock prefixes for everything. Buffer overflows are undefined so that there aren't bounds checks inserted everywhere. And so forth.

Undefined Behaviour as usual

Posted Feb 23, 2024 20:04 UTC (Fri) by Wol (subscriber, #4433) [Link]

> And every compiler that people actually use is optimizing.

And this is an untrue generalisation :-)

Admittedly I don't use it much, but a lot of people make great use of it - I guess many of the DataBASIC compilers are not optimising. I know OpenQM isn't. The guy who wrote it is documented as saying the extra complexity involved wasn't worth the candle.

Okay, it's probably still vulnerable to optimisation, because DataBASIC compiles to high-level p-code, which is then processed by an interpreter written in C ... but that ain't an optimising compiler.

Cheers,
Wol

Off-topic

Posted Feb 23, 2024 20:37 UTC (Fri) by geofft (subscriber, #59789) [Link]

LWN really needs to implement downvotes / flagging, so when someone says "This comment you are making about C compilers, in a thread entitled 'undefined behavior,' is possibly untrue for this one thing that isn't a C compiler and is for a language that does not have undefined behavior," it doesn't permanently stain the comment thread.

Off-topic

Posted Feb 24, 2024 5:12 UTC (Sat) by willy (subscriber, #9762) [Link]

Oh, I thought LWN's flag for"This comment contains no content of value" was to set the author to "Wol"

Undefined Behaviour as usual

Posted Feb 23, 2024 21:26 UTC (Fri) by tialaramex (subscriber, #21167) [Link]

> Signed overflow being undefined behavior is a little bit silly because a well-intentioned optimizing compiler will only use that optimization for one purpose: to emit whatever the underlying CPU's most efficient arithmetic instructions are to handle the non-overflowing case.

This does seem to be the expectation of many C++ programmers and I'd assume also C programmers.

It's wrong though. Here's a very easy example, the compiler just constant folded your arithmetic overflow out of existence... https://godbolt.org/z/v4evh3eEG

Undefined Behaviour as usual

Posted Feb 23, 2024 22:44 UTC (Fri) by khim (subscriber, #9252) [Link]

> Signed overflow being undefined behavior is a little bit silly because a well-intentioned optimizing compiler will only use that optimization for one purpose: to emit whatever the underlying CPU's most efficient arithmetic instructions are to handle the non-overflowing case.

Sigh. I wonder if “we code for the hardware” guys would ever learn that “well-intentioned optimizing compiler” is an oxymoron, it just simply couldn't exist and doesn't exist. Compiler couldn't have intentions, well-intentions or ill-intentions. It's just simply basis of the whole compiler theory.

That discussion happened more than decade ago and it's still relevant. And if you think that gcc have, suddenly, become “well-intentioned” simply because GCC12 or GCC13 don't turn that particular example into pile of goo then you are sorely mistaken: it's only because these have learned to [ab]use SIMD instructions in -O2 mode. Take that away and we are hack to square one.

At this point we should stop pretending C and C++ are salvageable because trouble with them is social, not technical: even after decades of discussions “we code for the hardware” crowd is not ready to give up on their dream of “well-intentioned” compiler while compiler makers are not even trying to discuss changes in the language which may help these people to produce working programs.

Undefined Behaviour as usual

Posted Feb 24, 2024 1:08 UTC (Sat) by tialaramex (subscriber, #21167) [Link]

I've replied more specifically about signed overflow because at this point it's a trope.

More generally though over the past say five years I've become increasingly comfortable with the "demons fly out of your nose" characterisation despite the fact that yes, technically that specifically won't happen (because demons aren't real). The characterisation is appropriate because it inculcates the appropriate level of caution, whereas the "It will never do anything unreasonable" guidance you prefer reassures C and C++ programmers that they're safe enough when in fact they're in constant danger and _should_ be extremely cautious.

There's an Alisdair Meredith talk which I can't find right now where Alisdair confidently explains that your C++ program cannot delete all the files on a customer's hard disk unless you wrote code to delete all their files - He argues that while sure as a result of UB it might unexpectedly branch to code you wrote that shouldn't normally run, or pass different parameters than you expected; it cannot ever just do arbitrary stuff. This is of course completely untrue, I'm guessing every LWN reader can see why -- but it does make it easier to feel relaxed about having mountains of crappy C++ code. Sure it has "Undefined Behaviour" but that probably just means it will give the wrong answers for certain inputs, right?

If every C and C++ programmer had the "Ralph in danger" meme image on a poster over their desk I'd feel like at least we're on the same page about the situation and they've just got a different appetite for risk. But that's not the world we have.

Undefined Behaviour as usual

Posted Feb 24, 2024 1:23 UTC (Sat) by pizza (subscriber, #46) [Link]

> He argues that while sure as a result of UB it might unexpectedly branch to code you wrote that shouldn't normally run, or pass different parameters than you expected; it cannot ever just do arbitrary stuff. This is of course completely untrue,

No, it is categorically true, because "undefined" does not mean "arbitrary and unbounded"

Using your logic, triggering UB means the computer could respond by literally exploding. Ditto if your computer gets hit with a cosmic ray.

If you argue that "no, the computer can't do that because nodody built explosives into it", why can't that argument also be applied to UB arbitrarily deleting your files instead? Sure, both are _possible_ in the sense that "anything is possible" but you're far more likely to have your car hit by a train, an airplane, and a piece of the International Space Station... simultaneously.

Undefined Behaviour as usual

Posted Feb 24, 2024 2:03 UTC (Sat) by tialaramex (subscriber, #21167) [Link]

I wasn't expecting I'd have to explain this here, but I guess I have to be disappointed.

For the file deletion situation the usual way this comes up is that bad guys hijack a program (whatever its purpose may have been) to execute arbitrary code (not something it was intended to do, but ultimately not hard to achieve in some UB scenarios as numerous incidents have demonstrated). Then they do whatever they like, which in some cases may include deleting your files (perhaps after having preserved an encrypted "backup" they can sell to you).

Undefined Behaviour as usual

Posted Feb 24, 2024 3:12 UTC (Sat) by pizza (subscriber, #46) [Link]

>Then they do whatever they like, which in some cases may include deleting your files (perhaps after having preserved an encrypted "backup" they can sell to you).

Seriously? Calling that the consequence of "undefined behaviour" is beyond farcical, as the _computer operator_ is *deliberately choosing* to delete files.

Just because the operator is unauthorized doesn't make them not the operator.

And "undefined behaviour" is not a requirement for, nor does it necessarily lead to, arbitrary command execution.

Undefined Behaviour as usual

Posted Feb 23, 2024 17:37 UTC (Fri) by mgb (guest, #3226) [Link]

Not 100% facetiously: Maybe a CVE should be filed against Undefined Behavior in the C standard instead.

Undefined Behaviour as usual

Posted Feb 23, 2024 18:05 UTC (Fri) by wtarreau (subscriber, #51152) [Link]

To be honest, it should be against compilers that abuse the original UB that was mostly "depending on your CPU it will be different" and that turned it into "haha here's a potential though unlikely UB, let's just drop this entire code block that protects against a vulnerability so that we can appear faster than competition in benchmarks".

UB makes sense for signed integer overflows that might return -x or ~x depending on the machine's twos complement. I.e. "we trust that you don't care about the result past this limit". It makes sense for shifts with high bits set, for the same reason (some CPUs will zero the output, others will mask higher bits), etc. The absurd approach of "the program might burn your house" has been causing a lot of harm to a ton of perfectly behaving programs on their usual platforms.

Sure, some optimizations are based on this. But instead of UB they could have presented the known possible outputs (e.g: what acceptable values will x+1 have). It would have permitted the same optimizations without purposely ruining valid tests. And actually, compilers could agree on this without even revisiting the standard.

Undefined Behaviour as usual

Posted Feb 23, 2024 22:53 UTC (Fri) by khim (subscriber, #9252) [Link]

> And actually, compilers could agree on this without even revisiting the standard.

They could, but what would it change? The real problem is not the fact that C and C++ have so many undefined behaviors but the fact that there are so many people with attitude if your language would have rules that I wouldn't like then I would just go and break them, anyway.

If C developers don't even contemplate the idea that they may, gasp, play by the rules, then what may change in the compiler ever achieve?

If you may, somehow, identify these people, and then kick them out of your community like Rust did then this exercise starts becoming useful, but I don't believe that may ever happen in C or C++ communities. “We code for the hardware” crowd is too entrenched and influential there.

Undefined Behaviour as usual

Posted Feb 23, 2024 18:36 UTC (Fri) by geofft (subscriber, #59789) [Link]

You are correct about it being undefined behavior, and you are correct that a compiler can cause the program to behave arbitrarily in response to it, but I would argue it is still not a vulnerability.

A vulnerability is a misbehavior in a program that compromises its security in response to crafted input / malicious actions from an attacker. This requires a few things to exist. "Misbehavior" requires a coherent concept of intended behavior. "Attacker" and "crafted input" require a system that interacts with untrusted parties in some way. "Security" requires things that you're trying to keep secure (maintain confidentiality, integrity, and/or availability) from the attacker.

As an obvious example, the fact that bash executes arbitrary code is not an arbitrary code execution vulnerability. It's what bash is supposed to do. The thing that supplies input to bash is expected to be the legitimate user of bash, not an attacker. bash is supposed to be able to run any command, and so it doesn't have a distinction between behavior and misbehavior. If you can get arbitrary code execution in rbash, for instance, then yes, that's a vulnerability, because rbash is designed to take untrusted input and maintain security of the system it runs against that input. If you can run arbitrary commands by setting environment variables even if you can't control input, then there's probably a vulnerability (as Shellshock was). But for regular bash, if you are setting up a system where you're piping untrusted input into bash, that's a vulnerability in your system, not in bash.

The only way to distinguish that is to know at a human level what bash is supposed to do and where it's supposed to take inputs from. There is no automated technical distinction to be made between bash and rbash. There is no automated technical distinction to determine that commands like bash and python are only intended to be run with trusted input, but commands like grep and sort are supposed to be able to handle untrusted input. You can call this a "gut feeling" if you like, but it's inherent to how we use computers. We never run a program for the sake of running a program; we run a program in order to accomplish some task of human interest, and the parameters of that task, not the program, determine what counts as misbehavior and insecurity.

There is a simple argument that CVE-2020-19909 (note there is a typo in today's article, it's 19909, not 1909) is not a vulnerability. It is not that the undefined behavior doesn't exist, or that the risk of a compiler doing something arbitrarily weird and unwanted is low. It is entirely compatible with a compiler generating code to do system("rm -rf /") in the case of signed integer overflow. The argument is that attackers do not have access to set an arbitrary retry delay value, and any real-world system that uses curl where attackers do have this access has a more fundamental vulnerability - e.g., they can provide arbitrary arguments to the curl command line, or they already have code execution in an address space in which libcurl is linked. Even in the most limited case where the attacker can only specify this one value and nothing other than curl is imposing limits on the value they'd still be able to effectively request that curl should hang for 24 days on a 32-bit system or until well past the heat death of the universe on a 64-bit system, which is already a denial of service vulnerability, and fixing that would avoid hitting the integer overflow in curl. And if you, yourself, as the legitimate user of curl or libcurl provide a ludicrous value for the timeout, it cannot be said to be a vulnerability, because you are not an attacker of your own system.

How do we know if this claim is correct? You're right in a technical sense that you can't really know - it's always theoretically possible that someone wrote a web form that takes input from untrusted users and one of the fields is the retry vulnerability value But it's also equally theoretically possible that someone wrote a web form that takes input from untrusted users and splices it into a bash command line without escaping. And, in fact, it's not just theoretically possible, it's quite common. But nobody would say this means there's a vulnerability in bash, would they?

The process of determining what is or isn't a vulnerability has to come down to human judgment. You could plausibly argue, for instance, that Shellshock wasn't a vulnerability because attackers shouldn't be able to pass arbitrary environment variables into bash. But the real-world deployment of CGI meant that there was a large installed base of users where attackers were in fact able to set environment variables to arbitrary values. Moreover, it meant that humanity believed that it was a reasonable design to do that, and the flaw was not with CGI for setting those variables.

And it's not sufficient to just lean on documented behavior. First, would you consider it an adequate fix for the vulnerability if curl made no code changes but just changed the documentation to say that the input to the timeout value must be small enough to avoid integer overflow? But also there have actually been real-world vulnerabilities, that are unquestionably vulnerabilities in human judgment, that were documented. Log4shell comes to mind: the behavior of loading JNDI plugins in log statements was absolutely intentional, and the support in JNDI for loading plugins from arbitrary servers was also absolutely intentional. But the resulting behavior was so unreasonable that the Log4j authors did not argue "there is no deviation from the documented behavior" - which they could have argued with much more certainty than a gut feeling. Or consider the KASLR bypass from the other day: it isn't material whether the kernel developers intended to publish a world-readable file with post-ASLR addresses or not, it is still a security bug either way.

There is, simply, no way to determine what is or isn't a vulnerability without the involvement of human judgment. You can make a reasonable argument that the maintainers of the software are poorly incentivized to make accurate judgments, yes. But someone has to make the judgment.

(Also - I actually fully agree with you about CVE-2023-52071. The argument that it only applies in debug builds and not release builds is reasonable as far as it goes, but in my human judgment, it is totally reasonable to run debug builds in production while debugging things, and you're right that Daniel's claim that it can only possibly cause a crash is incorrect. Because the bad code deterministically does an out-of-bounds access, it's totally reasonable for the compiler to treat the code as unreachable and thus conclude the rest of the block is also unreachable, which can change the actual output of the curl command in a security-sensitive way. The compiler can tear out the whole if statement via dead-code elimination, or it can lay out something that isn't actually valid code in the true case, since it's allowed to assume the true case never gets hit. He's quite possibly right that no compiler actually does that today; he's wrong that it's reasonable to rely on this.)

Undefined Behaviour as usual

Posted Feb 23, 2024 19:54 UTC (Fri) by adobriyan (subscriber, #30858) [Link]

> Because the bad code deterministically does an out-of-bounds access, it's totally reasonable for the compiler to treat the code as unreachable

How is it reasonable? If compiler can prove OOB access to on-stack array, it should _refuse_ to compile and report an error.

The only semi-legitimate use case for such access is stack protector runtime test scripts (and even those should be done in assembler for 100% reliability).

> warning: array subscript 3 is above array bounds of ‘wchar_t[3]’ {aka ‘int[3]’} [-Warray-bounds=]

int f(void);
int main(int argc, char *argv[])
{
wchar_t prefix[3] = {0};
if (f()) {
assert(prefix[3] == L'\0');
}
return EXIT_SUCCESS;
}

Undefined Behaviour as usual

Posted Feb 23, 2024 21:16 UTC (Fri) by geofft (subscriber, #59789) [Link]

I gave a somewhat contrived example in this other comment. It is entirely possible that the OOB-ness of the access is conditional in some way, such as via preprocessor macros or code generation from some template, and the programmer knows that f() is not going to actually return true in the case where the access would be out of bounds.

Here's another example, though you might argue that it is also contrived. Suppose you have a binary format that stores numbers between 0 and 32767 in the following way: if the number is less than 128, store it in one byte, otherwise store it in two bytes big-endian and set the high bit.

inline int is_even(unsigned char *p) {
    if (p[0] & 0x80)
        return p[1] % 2 == 0;
    return p[0] % 2 == 0;
}

unsigned char FIFTEEN[] = {0x15};

if (is_even(FIFTEEN))
    printf("15 is even\n");

After inlining there's a line of code talking about FIFTEEN[1] which is out of bounds, inside an if statement, just like your example. The if statement doesn't succeed, so there's no UB, but you need to do some compile-time constant expression evaluation to conclude that, and it's pretty reasonable, I think, to have a compiler that supports inlining but does no compile-time arithmetic.

Undefined Behaviour as usual

Posted Feb 24, 2024 5:32 UTC (Sat) by adobriyan (subscriber, #30858) [Link]

It is probably less work to just emit potentially UB access and let pagefault handler sort it out.

Undefined Behaviour as usual

Posted Feb 23, 2024 22:58 UTC (Fri) by khim (subscriber, #9252) [Link]

> How is it reasonable? If compiler can prove OOB access to on-stack array, it should _refuse_ to compile and report an error.

Such approach is incompatible with SPEC CPU 2006 which means none of compiler vendors would ever release such a compiler.

Undefined Behaviour as usual

Posted Feb 24, 2024 5:09 UTC (Sat) by adobriyan (subscriber, #30858) [Link]

There should be translate.google.com but for programming languages.

Stenberg: DISPUTED, not REJECTED

Posted Feb 23, 2024 19:56 UTC (Fri) by ewen (subscriber, #4772) [Link]

As far as I can tell the curl project did succeed in getting this “CVE” (CVE-2023-52071) rejected, although their statement about it (linked in the original post) doesn’t seem to have been updated to reflect that new status yet. Both of:

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-5...
https://nvd.nist.gov/vuln/detail/CVE-2023-52071

show “REJECTED” status for me now (and minimal details on what the original claimed vulnerability was in the change history). It seems to have changed earlier today.

So it seems curl becoming a CNA did give them some additional leverage to reject “CVE” reports they thought were bogus.

But the curl project and others are right that the CVE system is pretty broken. And basically designed for the opposite of today’s situation, ie designed to track good faith reports against vendors unwilling to fix real problems. Sadly there’s a lot of momentum behind the CVE system now, so I don’t see it being fixed any time soon. And all vendors just being able to arbitrarily reject any report without documentation is itself vulnerable to abuse too.

Stenberg: DISPUTED, not REJECTED

Posted Feb 23, 2024 21:34 UTC (Fri) by bagder (guest, #38414) [Link]

Thanks, I had not been told about this. I have now updated the blog post with a mention of this fact. Wow. I did not expect it to happen (this quickly).

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK