C-Based Toolchain Hardening

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

C-Based Toolchain Hardening

Ben Coman
 
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

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."

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
* areas that could be improved
* are adverse to personal experience

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

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??

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

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: C-Based Toolchain Hardening

Eliot Miranda-2
 
Hi Ben,

On Fri, Sep 2, 2016 at 5:51 AM, Ben Coman <[hidden email]> 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