Zcoin implementation bug enabled attacker to create 548, 000 Zcoins

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Sat Mar 11 11:28:16 PST 2017

On Sat, Mar 11, 2017 at 03:03:30PM +0000, Nick Treleaven via Digitalmars-d wrote:
> On Friday, 10 March 2017 at 19:02:06 UTC, H. S. Teoh wrote:
> > A long-standing item on my todo list is to implement compile-time
> > writefln format strings using this technique.
> Yes, the actual checking seems straightforward - here I implemented CT
> format as an overload:
> import std.format : format;
> auto format(string fmt, A...)(A args)
> {
> 	static assert(__traits(compiles, {enum s = .format(fmt, A.init);}),
> 		"Arguments do not match format string: '" ~ fmt ~ "' with " ~ A.stringof);
> 	return .format(fmt, args);
> }
> unittest
> {
> 	auto s = format!"%s is %s"(5, "five");
> 	static assert(!__traits(compiles, {s = format!"%d"("four");}));
> }

Yes, that's the general idea of it.

But I want to go further: to eliminate unnecessary dependencies of
format() based on the contents of the format string.

More specifically, currently calling format() will instantiate a whole
bunch of stuff for typesetting *all* possible format strings, like
floating-point, array expansion, etc.. That means a simple
format("%s",s) will pull in a whole bunch of code into your executable
that may not actually be used. (And not even LTO can get rid of it,
because the code is inside if- or switch-statements, and technically is
"referenced" by the caller; the linker has no way to know it won't
actually get called.)

Not to mention, "%s" itself pulls in a whole bunch of code for dealing
with things like field width, max width, padding, etc. (i.e., to handle
things like "%10s", "%10.5s", and so on), all of which isn't actually
needed to implement plain ole "%s".

So the idea is to analyse the format string at compile-time to determine
exactly what functionality is actually used, and instantiate only that.
Think of it as a format-string mini-compiler: given a format string and
a list of argument types, compile it into the equivalent minimal D code.

	format("abc%sdef", s)

should get compiled into:

	"abc" ~ s ~ "def"	// N.B.: no floating-point code, no
				// width handling, etc.

and so on.


What doesn't kill me makes me stranger.

More information about the Digitalmars-d mailing list