[Vm-dev] C-Based Toolchain Hardening

Eliot Miranda eliot.miranda at gmail.com
Fri Sep 2 09:56:47 UTC 2016


Hi Ben,

On Fri, Sep 2, 2016 at 5:51 AM, Ben Coman <btc at openinworld.com> wrote:

>
> The more experienced here probably know a lot of this, but I found it
> quite interesting so I thought I'd share...
>
> https://www.owasp.org/index.php/C-Based_Toolchain_Hardening


TL;DR, but I skimmed it.

My biggest take away is the emphasis on using asserts.  My
> professional C programming experience was only a short stint 20 years
> ago in a graduate position where asserts were never discussed.  So
> I've never used asserts and it was particularly enlightening to
> read... "in the time it takes for you to write a printf or NSLog
> statement, you could have written an assert. Unlike the printf or
> NSLog which are often removed when no longer needed, the assert stays
> active forever."
>

+1


> In relation to my own (potential) contributions to the VM, I'd be
> interested to learn if there are any particular aspects from that
> article that particularly:
> * resonate with current practices
>

Sure.  In ParcPlace I moved from just having Debug and Release builds to
Debug, Assert (-O1) and Production builds cuz often the Debug build was
frustratingly slow.


> * areas that could be improved
>

With the appropriate setup for third-party libraries we can use Debug
builds of third-party libraries with Debug and Assert VM builds, and
Production builds of third-party libraries with the Production VM builds.

* are adverse to personal experience
>

No.  This is good style.  The code is both better commented (asserts often
state important invariants) and the VM is effectively full of tests.  This
is at least one reason why we get away with not having micro tests for VM
subcomponents, which are difficult to write since the VM is designed to
consume an entire Smalltalk image and execute an entire Smalltalk system.

Of course there are already production, debug and assert builds.  And
> it concurs with what I've read on the list about avoiding autotools.
>

Good to hear!


> The problem with dependency rebuilds when CFLAGS change between
> production/debug builds was enlightening (if obvious in hindsight).  I
> guess this is why each build type has its own directory.
>

Exactly.  The builds must be kept separate; otherwise they would pollute,
causing confusion and delay ;-)


> One thing I don't quite get is "Everywhere you place an if statement
> for validation or checking, you should have an assert. Everywhere you
> have an assert for validation or checking, you should have an if
> statement. They go hand-in-hand."
> Indeed, perusing asserts in the repository [1] doesn't show any nearby
> if statements, so maybe that statement is a stretch??
>

The assert scheme includes assert for use in ifs.  A normal assert is an
expression that produces a warning if it is false, and is compiled only in
debug and assert builds.  An asserts is an expression that produces a
warning if it is false in debug and assert builds, but is still evaluated
in all builds.  So if you want an if combined with an assert you use
asserta, e.g. see

CoInterpreter>>callbackEnter: callbackID
"Re-enter the interpreter for executing a callback"
| currentCStackPointer currentCFramePointer savedReenterInterpreter
 wasInMachineCode calledFromMachineCode |
<volatile>
<export: true>
<var: #currentCStackPointer type: #'void *'>
<var: #currentCFramePointer type: #'void *'>
<var: #callbackID type: #'sqInt *'>
<var: #savedReenterInterpreter type: #'jmp_buf'>

"For now, do not allow a callback unless we're in a primitiveResponse"
(self asserta: primitiveFunctionPointer ~= 0) ifFalse:
[^false].

self assert: primFailCode = 0.

"Check if we've exceeded the callback depth"
(self asserta: jmpDepth < MaxJumpBuf) ifFalse:
[^false].
jmpDepth := jmpDepth + 1.
...

So the asserts are eliminated in production builds and the assertas remain
but do not produce a warning, so the code is equivalent to

callbackEnter: callbackID
"Re-enter the interpreter for executing a callback"
| currentCStackPointer currentCFramePointer savedReenterInterpreter
 wasInMachineCode calledFromMachineCode |
<volatile>
<export: true>
<var: #currentCStackPointer type: #'void *'>
<var: #currentCFramePointer type: #'void *'>
<var: #callbackID type: #'sqInt *'>
<var: #savedReenterInterpreter type: #'jmp_buf'>

"For now, do not allow a callback unless we're in a primitiveResponse"
primitiveFunctionPointer ~= 0 ifFalse:
[^false].

"Check if we've exceeded the callback depth"
jmpDepth < MaxJumpBuf ifFalse:
[^false].
jmpDepth := jmpDepth + 1.


You'll also find eassert: which is for expensive asserts that are evaluated
only if enabled.
In the simulator this is controlled by the VMClass class
var ExpensiveAsserts.
In compiled C VMs this is controlled by the expensiveAsserts variable,
which must be set by hand in lldb/gdb et al.

[1] https://github.com/OpenSmalltalk/opensmalltalk-
> vm/search?l=c&q=assert&type=Code&utf8=%E2%9C%93
>
> cheers -ben
>

_,,,^..^,,,_
best, Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/vm-dev/attachments/20160902/4a4976da/attachment.htm


More information about the Vm-dev mailing list