Zcoin implementation bug enabled attacker to create over 500K Zcoins
makebitcoingreatagain.wordpress.com
makebitcoingreatagain.wordpress.com
C++ is not memory-safe (no GC or whatever Rust does), it's not type safe (in the ML sense), it relies on writing to memory a lot (instead of having pure functions). Had this program been written in Ocaml or Haskell or a strongly typed functional Lisp, this bug would have been impossible to make.
Why do we do this, still? Is it not irresponsible?
Using 'unmanaged' languages for 'performance' reasons is no longer a good reason, because the likes of C# have shown multiple times that there is no reason to choose C++ over C# if you look at performance alone (difference is neglible).
http://benchmarksgame.alioth.debian.org/u64q/compare.php?lan...
Sure, it's silly benchmarks, so I wouldn't take the results as gospel. But the fact is, nobody who writes C# managed to produce a benchmark yet that beats a C++ program.
Fast languages like Rust have managed to at least match the performance of C in some benchmarks (and even beat it in others).
http://benchmarksgame.alioth.debian.org/u64q/program.php?tes...
How should /unsafe be set with .Net CORE csproj ?
"buildOptions": {
"allowUnsafe": true
}
I was able to fake something for dotnet migrate and find the needed setting: <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>http://benchmarksgame.alioth.debian.org/u64q/program.php?tes... http://benchmarksgame.alioth.debian.org/u64q/program.php?tes...
No, but I've seen Haskell programs that were much faster than the C++ program they replaced, which is what matters in the real world.
Microbenchmarks measure what happens if you have the time to polish every single line to perfection. If you have infinite developer time then C++ will be faster than most languages. But infinite developer time is a negligible use case.
The bug happened in "inlined constructor" so they should check all uses of this class/struct to check if it is not copied to other places (and probably all other structs/classes).
Companies exist to make money so the most responsible thing to do is make as much money as possible. If breaking the law means you will make more money then do it.
See: a x b x c = x formula from Fight Club or Uber's entire business model.
Your assumption that we don't want bugs is probably why you are asking the question. Companies don't care about bugs, they care about losing money so if the bug doesn't cost money it's not worth fixing until it costs more than the fix.
I think this is actually very short sighted but that's just how the incentives align in our current system.
I make no claim to the philosophical implications of any of this.
ZCash was built upon the Bitcoin codebase. This inherits a lot of bad decisions. Moral purity, demanding they start over again from scratch, just isn't practical.
The bug in question could have been solved had the simply compiled with minimal static analysis -- by which I mean -Wall.
C/C++ is memory safe if you turn on dynamic checking. Sure, it's twice as slow as C/C++, but still tons faster than nonsense languages like Ocaml or Haskell.
Is anyone here doing this? Would be interesting to hear your experience.
valgrind --quiet --leak-check=full --error-exitcode=1 *binary*What's the option to turn that on? Which compilers is it in?
I know there were several fat-pointer patches to GCC back in the day, but I didn't think anything remotely similar had ever gone mainstream. There's just too much existing code that relies on undefined behavior last I checked.
In any case, Zcash is also derived from Bitcoin and builds with `-Werror` (edit: not `-Wall`, but we're working on that). That kind of minimal static analysis is certainly not sufficient to catch the majority of bugs, though.
C++ is not memory safe in any meaningful sense. There have been efforts to define a memory-safe subset, but typical large codebases, including Bitcoin, do not come close to falling within that subset.
This case specifically, a lot of the issues that allowed this to happen were logic errors which would have been valid in most languages. A functional language would not have helped very much here.
C and C++ are still used because they work, and have huge ecosystems around them. There's a lot of tooling around them, the compilers build fast, code, and are available for pretty much every planet under the sun. This particular case, it would have helped a lot if the developers looked at the warnings of the questionable code they had made, but they seem to be of the "it compiles, ship it" type that would have let those things fly regardless of the language.
I think the lesser amount of libraries and lacking the chance of fine-tuning performance (but also your risk of screwing up your performance badly) is a very low price for the provably impossibility to have a mistake like this.
I'm just thinking back on my career, which has been full of bugs of all magnitudes. If I categorise my past bugs, there are many common patterns. Famous old-timers have names, like buffer overflows or memory leaks etc etc. Just not having the possibility to make those errors is such a freeing experience.
Also what's the big problem with no independent standardisation?
I think with the types of errors you mentioned, that part of it definitely is ecosystem. Look at OpenBSD, for example. They've built things like a safer malloc and fairly extensive patches to gcc that have caught a lot of errors and bugs. I think C/C++ can definitely get safer without changing that much, the ecosystem just needs to get a bit angrier about warnings, etc.
Standardization, and multiple implementations, means you can have multiple sets of compiler eyeballs looking over things. The zcoin bug, for example, emits a warning with the default settings of clang, but doesn't emit a peep with the default settings of gcc. For someone working with a lot of embedded code, it also means that there's a better chance that there's an implementation of the language ready to go when you need to move to a different platform.
It's just a lot of untested code. Surely even Ocaml and Haskell need tests.
IMO rote splitting usually doesn't simplify, it just hides the complexity.
As a c++ developer I would nope the hell out of there or spend a month fixing the warnings - depending on pay. No way would I work with something in that state long term.
> until somebody gets to fix all warnings which are not bugs.
Code that is filled with ignored warnings generally gets worse, not better. Having little to no quality standards does that.
You might even find some zero days just by addressing all the warnings.
There's a reason practically no one uses a memory-safe C++ implementation.
You can have code without any warnings in clang, gcc and msvc++ compilers and it is probably better than code tested with only one compiler.
--
[0] - even if by ignoring them, when you're absolutely sure what you're doing.
-Wall -Wextra -Werror
If these options are set before you write any code, then these problems don't arise.Also IMO you often want to explicitly disable a few of those from -Wall and -Wextra -- Looking at the last c++ project I wrote, I turned off a few things like `-Wno-unused-parameter` for example.
[0]: http://blog.schmorp.de/2016-02-27-tidbits-for-the-love-of-go...
As you get further into a project you tend to need subtler tools. But for the rough work when starting out, my preference is to be stricter, which can be relaxed as necessary, rather than starting out lax and reaching a point where tightening becomes Sisyphean.
-Weverything
Which does actually turn on all warnings (so you'll probably want to add things like -Wno-unused -Wno-padded).Under gcc, '-Wall -Wextra -Wpedantic' does not enable all warnings. There are still more than 40 flags you have to give if you really want all of them.
The reason why boggles my mind to this day.
Bitcoin was also created by someone using a Japanese pseudonym, same place anime comes from.
> Unfortunately when populating the denomination member variable, an equality operator was used instead of assignment, resulting in the denomination always being zero, as set in the class constructor.
So = Vs ==
Building with warnings on would also have caught it, but the fact that someone started a new project without this warning turned on shows that C++'s defaults around warnings are bad, which is an ecosystem ergonomics issue.
In Rust, x = y evaluates to (), not any of the values in x or y.
In Rust, numbers are not implicitly usable as booleans in conditionals.
I think the main advantage Rust has over OCaml is that it is syntactically more like C. But, hey, if it's what gets HM-style type inference to the masses, I'll take it.
So if you want to blame anything, blame the concept of expression statements, I guess?
Programming without state is something that has helped me personally make my code much better. I produce less bugs by far since I stopped relying on state for so many things. A lot of micro-optimisations are not doable for me. In exchange I guess I get to run things in parallell very easily. But mostly, I get fewer bugs, which makes my life and the lives of my customers better.
A lot of times it isn't actually any faster, and it's certainly more error prone.
The idea that you form a value by creating an uninitialized one and then filling in its fields is dumb. (I suspect the field value wasn't even 0 as such but rather undefined, and the implementation happened to make it 0?). Better languages have you initialize the value directly and you can't access it until it's fully initialized. C/C++ are just really bad at expressing nontrivial values, even value literals.
(Discarding a value ought to be an error too, though that's more cumbersome to work with)
So, just as in c++ there is a different operator of assignment and equality that requirs you to type a different thing. User error and testing failure.
Let's collectively put our seat belts on, colleagues!
If there was not state, the coder wouldn't had tried to set it. I don't think a language needs to have the `=` operator easy and close at hand. Had that not been the case, this bug (and oh so many others) would never had existed.
A cryptocurrency implementation like this doesn't need a language that makes state impossible. It needs static analysis, model checking, and proofs. This would be true even if it were written in your favorite functional language.
If you make your new coin based on a functional language, there's fewer people who'll be able to jump in and code.
c++ looks similar enough to the other {} languages that many people will have the skills to give it a go.
Unfortunately it's different enough that you can really mess things up, but you already know that.
Zero bugs around state, zero bugs around memory mgmt, zero bugs about error conditions not being handled, zero bugs due to some data being expected but not being written, etc.
The programmer admitted that he was a cryptography novice, and in fact a Haskell novice.
As a result the code he wrote is needlessly abstract - for one thing the guy uses Free monads and in turn ropes in template Haskell as part of his state model. I really have no idea what code he's generating.
The code has other features that make review challenging - for instance he doesn't qualify any of his imports so it's hard to tell where to look for the functions he's implementing.
Maybe you are better at auditing Haskell than I am. As DJ Bernstein writes in various places, one common exploit is to construct an elliptic curve Diffie Helman shared secret with input that isn't a curve point. I really can't tell if the guy is mitigating against this attack or not, but here you can have a look:
Isn't it true though, that with some techniques certain bugs are impossible to make? If you have not experienced it, I recommend you to seek it out and try.
One reason is security - if cryptography isn't done in constant time then there is the potential for timing attacks. This is why BitCoinJ ditched BouncyCastle for their own JNI wrapper around BitCoin's libsecp256k1.
There is a rust client for Ethereum, called Parity - they do FFI calls to libsecp256k1. Similarly rust-crypto vendors most of its crypto in C that it calls through FFI. Similarly go-ethereum also uses libsecp256k1. However, I know that Tendermint uses Ed25519 instead of secp256k1, and in turn they use NaCl which does not involve FFI.
Mining for Ethereum is done on a GPU, so the miner code is necessarily written in OpenCL or CUDA.
There are a few blockchains written in functional language but they are new to the scene. There were a couple of Haskell Ethereum clients but they appear to be abandoned. I know kadena[1] and cardano[2] are written in Haskell. There is also Aeternity[3], which is the only currency I know of written in Erlang.
In the early days Charles Hoskinson apparently wanted Ethereum to be written in Scala. I don't think any of the core devs know Scala, but then again Charles Hoskinson was booted from the project so his language choice was dropped.
Synereo was to be written in Scala, but the founders (the CEO and the CTO) split in December. The CEO doesn't want Synereo to make a blockchain anymore, he wants to make some kind of escrow for ponzi schemes. I hear from the devs they want to code the thing in javascript. The CTO is making something called RChain; I heard he wants to use some pi-calculus interpreter he wrote in C90 back when he worked at Miter corporation in the 90s, but maybe he's ditching that.
From job postings I can see MaidSafe is looking for Rust programmers. MaidSafe has been in R&D for a decade, so they must have had yet another pivot recently.
To be frank, while there are functional programmers in the scene, these guys are struggling to establish themselves. There's a lot more talent in procedural programming which is why so much code is written in those languages. I'm not 100% that functional code is going to have fewer bugs which result in financial exploits.
[1] http://kadena.io/ [2] https://iohk.io/projects/cardano/ [3] https://github.com/aeternity
I gave a talk with a summary of why they switched from C++ as part of this general talk on production Rust last year: https://www.infoq.com/presentations/rust-production
I assume Rust allows for the same type of error?
Not familiar with Rust, but does the following work?:
let mut x = 0;
if x = 1 {
println!("oops, meant ==");
} error[E0308]: mismatched types
--> src/main.rs:5:6
|
5 | if x = 1 {
| ^^^^^ expected bool, found ()
|
= note: expected type `bool`
= note: found type `()`
error: aborting due to previous error
error: Could not compile `playground`.
To learn more, run the command again with --verbose. fn main() {
let mut x = false;
println!("{}", x);
if {x=true;x} {
println!("{}", x);
}
}
But stands out as pretty obvious. Not something you might easily sneak into a codebase.æternity is a new blockchain for scalable smart contracts interfacing with real-world data via a decentralized oracle.
Smart contracts are essentially pure functions here. We have also a compiler for a Lispy language.
Check out our whitepaper (www.aeternity.com) and check out our Github ( github.com/aeternity/testnet ) (also check the /docs folder).
Disclaimer: I am the founder of aeternity.
Still? Since when? Last I checked it was 661kB for Rust vs 829kB for C, and that was quite a while ago.
https://github.com/zcoinofficial/zcoin/blob/81a667867b5d8489...
and the line of code:
zccoinSpend.denomination == libzerocoin::ZQ_LOVELACE;
In other words, a statement with no effect. In D, such a line gives an error, not a warning: Error: == has no effect in expression
You can force the statement to be accepted by casting it to void.It's really past time for languages to not accept such code any more. Legacy code needs to get fixed.
I can't think of a reason any modern language should accept such code.
If you're interested, I recommend the series "Air Disasters" on TV. Each episode analyzes one crash, where investigators determine the cause of the accident, and what the corrective action should be taken. Often, it comes down to "how could a pilot with 10,000 hours experience make such a mistake". While it would be easy for investigators to blame the pilot, often they find that the mistake was just too easy to make, and they recommend changing the design or procedures or both.
--accept_my_buggy_stupid_code
so nobody is going to type that in by accident, and nobody is going to want to defend it in the code review process. And the compiler vendor will still get a gold star for being 100% language compliant :-) test.cpp: In function ‘int main()’:
test.cpp:13:11: warning: statement has no effect [-Wunused-value]
a == b;
^
But only after enabling a warning that is not on by default. -Wall and -Werror should be the default. if (condition);
dothis();
No compiler should accept that, regardless of switch settings. if (condition);
Should already be enough to trigger the error. (Any 'if' statement without a body).In C++ you can overload '==' to do anything you want, including things that have side effects[0]. Without deeper introspection into the code, it's tricky to check if it's an error or not.
[0] PS. Never do this!
-Werror, I'm not sure. During development and on CI systems it makes sense. But if you intend to ship source code, it's probably best left off. Other people might be using different compilers that emit warnings for things yours didn't. You don't want the build to fail for those people.
Actually, you do. That way those things will be fixed and hopefully your inclusion of their fixes is a well motivated PR away.
Switching -Werror off should be a decision made with great care and understanding of what's going on under the hood. If there are platform specific issues then people on those platforms will be the ones in the best position to determine if such a warning is actually an error or not.
Having compilation break for a user because of a spurious warning due to a different compiler (or different compiler version) would make it a lot more painful to maintain.
For instance I started maintaining an old C++ codebase that generates a bunch of warnings when build with a modern g++, code like this:
#define M "toto"
const char *s = "tata"M;
Generates warnings like:warning: invalid suffix on literal; C++11 requires a space between literal and string macro [-Wliteral-suffix]
Is it helpful? Sure. It is worth breaking the build for anybody using a modern g++ until I manage to fix all occurrences and release a new version? I don't think so.
An other example is building code with -Wextra which warns you when some comparisons become "nops". For instance:
int toto(int i) {
if (i <= 0xffff)
return 1;
return 0;
}
On most modern machines this will build without a hitch, but if you compile on a 16bit machine you'll get a warning saying:warning: comparison is always true due to limited range of data type
In my experience this particular warning is generally not a problem. On the other hand the following code:
int i = 0xabcdef;
Will generate the following warning on 16bit architectures:warning: integer constant is too large for its type
And that's potentially a lot nastier and should be an error IMO.
So I'd say the main problem is that C and C++ are way too permissive by default, things like comparison with no side effects ought to be errors, not warnings. And there are a bunch of others, for instance why on earth are these not errors :
warning: no return statement in function returning non-void
warning: ‘i’ is used uninitialized in this function
So -Werror is just killing a fly with a bazooka IMO.Surely this is a serious problem on 16 bit builds; the comparison implies that larger values are expected to be stored in this variable, the warning is a strong hint that the (size of the) types are incorrect.
This, a hundred times. Building without proper warnings (and without proper tests, review) is just bad engineering, and mediocre coding.
It seems that D never really got the love it deserved, perhaps because it didn't have a Google or Mozilla behind it.
I feel it is my duty as a language designer to make it hard for programmers to commit common bugs. It's just too expensive for companies to ship these kinds of things.
https://github.com/zcoinofficial/zcoin/commit/0359bcb2ead7fe...
This indicates that the code in question is not covered by tests. I work on a database, for which correctness is paramount, so it's unnerving to see any code fixes not associated with test additions or changes. I would have thought that cryptocurrency had similar standards.
I'm guessing that this was meant to be a single equals sign for an assignment rather than a double equals for an equality test?
If you are a skilled programmer / security expert / mathematician, crypto currency exploit creation seems to be an extremely lucrative hobby.
Plus, the language in 18 U.S.C. § 1030(e)(2) (courtesy of CFAA) refers specifically to "any computer, when [it affects] use by or for [a] financial institution." And later it mentions "...affecting interstate or foreign commerce or communication...". This statute is one that gives so much leeway to prosecutors that it's frequently abused.
Perhaps the next generation of cryptocurrencies can have a simple EULA that prohibits exploitation and can be used to firewall off stolen coins.
Government censorship of coins or transactions is an attack vector, not a feature.
> next generation of cryptocurrencies can have a simple EULA
This is anathema to the nature of cryptocurrencies, all (?pretty sure all that matter anyways) of which have Open Source implementations.
Really, most cryptocoins which are worth anything have already had a big market cap "bounty" that has effectively proven their safety.
We don't need a EULA. Like I said, you could maaaybe sue for damages. Unfortunately there's no easy way to undo the impact to a coin's reputation once it's shown to have a weakness like this. Even once the bug's patched and the nodes all get back on board, exchange rates will suffer for a long time.
[1] https://en.wikipedia.org/wiki/Title_17_of_the_United_States_...
or in the case of Zcoin just come back into the clear chain.
I am genuinely curious as to what the arguments would be one way or another. People are taking a risk with these things and they know they're risky. So it's not really stealing.
Only if you're citizen of/doing it in a country with such laws.
I'm content getting my kicks out of intentionally running the occasional red light.
But it takes all types. Imagine how boring the world would be without crime!
When someone comes to play poker with chips, is it immoral to beat them and take their chips? No, they factored it in.
However, is it immoral to figure out a new collusion strategy and beat them?
Was it immoral to figure out a card counting strategy to beat casinos?
In other words, is it immoral to figure out new strategies in what is essentially an experimental game where participants assume there is risk?
In well established games like poker, which explicitly have gentlemen's agreements of "no collusion", it may be immoral.
But in a new experimental system such as a cryptocurrency whose software may be buggy, is taking advantage of the bugs really immoral?
When is it "buyer beware" and "a fool and his gold are soon parted"?
Isn't there any threshold for taking the money of a risktaker?
That's called a pyramid, or ponzi scheme.
Then compensate for that. Create a pool that pays out for each year without vulnerabilities.
I understand the network could implement a fix and prevent future transactions or even roll back old ones if there's enough desire, that's what democracies are about right?
But was there anything illegal about creating and then trading them? A rollback would hurt the exchanges, but if they didn't already have something in their T&Cs about currencies needing to be created legitimately (and what legitimately actually means), then is it anything other than morally wrong?
I'm not sure there's much that couldn't
(I'm not sure how this relates to courts of law.)
One important distinction to make is that it really is also going to depend on the jurisdiction of those involved. Is the defendant in the US or somewhere else? Who is prosecuting and what are the charges?
Not the same, but it reminded me of years ago, I had £50 deposited in to my account, by mistake, a couple of months running. I asked my girlfriend's dad - who actually was AL (WAL?) - if I could keep it. He said it would be like someone parking their car on my driveway and leaving the keys in the ignition. Inconvenient, yeah, and I don't want it there - but it doesn't make the car mine.
If a private person sends you money, you have to give it back.
So if a bank in Poland sends you a million dollars by mistake, is it yours?
What would happen if someone stole it and transferred it to your account, does that make you a criminal?
And even more interestingly, if a malicious bank programmer (who's in league with you) introduced a bug on purpose (let's suppose we cannot prove the intent here, it's very difficult) and the bank mistakenly deposits you the money, do you have a duty to return it?
So only 31 other cases to test for, now let's hope they did all those tests.
Easy. Either there is no code review, the code review is done by someone who has far too much on his plate a works on the theory that as long as it compiles and runs it's probably fine or the code review is done by someone likes writing code with five conditions ANDed together in an if and thinks it's fine
I mean, all the mathematical rigor and proofs of the underlying theory and protocols are basically useless if the rigor isn't carried over to at least the reference implementations. (If people choose to use alternative, non-proven clients, fine, that's their own decision and risk.)
What I'm trying to say here is, the whole cryptocurrency field is much, much messier than you seem to expect.
It was a disaster. Some founders started selling their Zcoin as soon as it was listed on an exchange. The mining algorithm was changed several times.
It's a shame. Zerocoin, the underlying technology, is interesting.