Typical security issues in C++: why the GC isn't your enemy
Siarhei Siamashka
siarhei.siamashka at gmail.com
Mon Dec 12 11:07:00 UTC 2022
On Monday, 12 December 2022 at 03:48:26 UTC, Walter Bright wrote:
> On 12/9/2022 2:39 PM, H. S. Teoh wrote:
>> According to `dmd -h`, they are disabled everywhere except
>> @safe code.
>
> Safe code is where bounds checking is needed.
>
>> And arguably, @system code is precisely where you WANT to have
>> bounds
>> checks.
>
> Use of @system code should be minimized and in it you're
> allowed to do whatever you want.
You have my 100% support here. But the tricky thing is that the
majority of the current D language community seems to disagree
with us, as evidenced by the apparent rejection of DIP 1028. And
we should do a much better job convincing them to change their
mind.
What kind of dangers are threatening the users of @system code?
This needs to be explained better. Especially considering that
every beginner pretty much starts using @system code by default
even before knowing that there's a choice.
=== use after free ===
Right from the top of the Chromium list, the "use after free"
security problem. Have a look at [this forum
thread](https://forum.dlang.org/post/wssohnwydjgsouywbitk@forum.dlang.org). Presumably a beginner is asking about how to deal with a c-string returned by the sqlite3 library. And many of the forum regulars are very happily suggesting to use `std.conv.fromStringz` or some other ways of turning the pointer into a slice while avoiding a GC allocation. One person even believed that `std.conv.fromStringz` is doing a GC allocation. Now if this string is saved somewhere instead of just printing it, then we potentially have a "use after free" problem, because this memory is managed by sqlite3 and will get a `realloc` treatment by the follow-up sqlite3 API calls (I may be wrong, but that's what it looks like after quickly skimming through the sqlite3 source code). This is a potentially dangerous stuff and it mostly affects the @system code because the compiler remains silent about a lot of fishy things.
Also see the https://github.com/dlang/dmd/pull/14639 pull
request. Some of the functions used to be incorrectly annotated
as @trusted and this was fixed later.
The users (and especially beginners!) need to annotate their code
as @safe before receiving any help from the compiler related to
this problem.
=== references to stack variables escaping the scope ===
Here's a good example
https://forum.dlang.org/post/duwrxnkjaafnzpfgnted@forum.dlang.org
posted by someone, who is not a complete beginner (I can see
their post
https://forum.dlang.org/post/pdjvwvzysetndrtkxmea@forum.dlang.org
from many months earlier). Looks like this person was not aware
of the @safe attribute all this time. And possibly started
experimenting and asking questions only after running into
troubles and having to debug some real code.
Another interesting observation is that despite my recommendation
to add "@safe:" at the top of the source file, this person still
plastered the @safe attribute all over the place in the next code
snippet. Also not recognizing this syntax doesn't seem uncommon:
https://forum.dlang.org/post/ddhxlvprhdpqrhkbxuyb@forum.dlang.org
=== bounds checking ===
Bounds checking is not done in @safe and @trusted code in
"-release" builds. How long does it typically take for a beginner
to learn about this? And how many of them learn it proactively by
reading the tutorials/documentation rather than getting
surprised/ambushed by an unexpected segfault in their release
build? This reminds me about
https://forum.dlang.org/post/couzhdooeskwppuklasf@forum.dlang.org
Now let's look at the DUB ecosystem and what kind of options are
used for compiling the existing DUB packages. Running `dub build
--help` shows:
-b --build=VALUE Specifies the type of build to perform.
Note that
setting the DFLAGS environment variable
will override
the build type with custom flags.
Possible names:
debug, plain, release, release-debug,
release-nobounds, unittest, profile,
profile-gc,
docs, ddox, cov, unittest-cov, syntax
and custom
types
Trying these build types in verbose mode reveals that the
following command line options are supplied to DMD:
debug = "-debug -g -w ..."
plain = "-w ..."
release = "-release -inline -O -w ..."
release-debug = "-release -g -inline -O -w ..."
release-nobounds = "-release -inline -noboundscheck -O -w ..."
Basically `-O` is always bundled together with `-release` and DUB
packages built with optimizations won't have bounds checking in
@system code. Of course, unless the packages explicitly override
this in their sdl/json.
So we are in a rather weird situation. At least in the DUB
ecosystem a lot of applications and libraries de-facto may have
unnecessary security issues caused by having too much @system
code. The community doesn't want to change the default from
@system to @safe. And at the same time the same community wants
to promote D as a safe language. People do get upset when, let's
say, NSA is not listing D as an example of a safe language.
=== how to fix all of this ===
My suggestion is still the same: the compiler should start making
noise whenever a function gets the @system attribute assigned to
it by default. Similar to deprecation warnings. This message does
not have to abort compilation and the existing DUB packages
should still build successfully (despite the '-w' option). The
text of the message may look like this:
"The function 'foobar' got @system attribute by default. If
this is really what you want, then please add '@system:' at the
top of the source file or read this <URL> for detailed
explanations."
I think that this will provide a gentle push/reminder for the
maintainers of the existing packages. Also beginners will learn
about the @safe attribute much faster.
More information about the Digitalmars-d
mailing list