valueDiff for arrays?
Mark Waddingham
mark at livecode.com
Sat Aug 4 13:41:29 EDT 2018
On 2018-08-04 19:19, J. Landman Gay via use-livecode wrote:
> This caught my attention. The increased security is great, and I've
> heard it said before, but would love to hear the reasons that make it
> more secure than lower level code. That way I'd have a knowledgeable
> reason to be smug.
It is impossible to write LCS code which *just* manipulates values which
will crash/vulnerability because of the code you have written in LCS
*unless* there is actually a crash/vulnerability bug in the engine in
one of the core operations which you are using.
If you translate an operation to C++, though - it is far far easier to
introduce crashes and thus vulnerabilities - this is because C++ does
not insulate you from the fundamentals of memory management, pointers or
all that stuff which comes with writing in a language which is (in
reality) only a short step away from machine code.
Admittedly, if you are using C++ as modern C++ then it is also quite
hard to introduce such things (assuming you have built the abstractions
using templates and C++'s typing system to do so correctly and use them
uniformly and correctly) - but you still *can* write code that does as
you can always by-pass C++'s typing and such - also the engine is not
modern C++ - it is C which was transformed in C++ and due to various
requirements on usage (at the lower-level) of the engine - many APIs
used internally in the engine (e.g. libfoundation) are actually C APIs.
For example. Here is the core engine code for array intersection (which
actually covers intersection, recursive intersection and difference):
static void MCArraysDoIntersect(MCExecContext& ctxt, MCValueRef p_dst,
MCValueRef p_src, MCArrayDoIntersectOp p_op, MCValueRef& r_result)
{
if (!MCValueIsArray(p_dst))
{
r_result = MCValueRetain(p_dst);
return;
}
if (!MCValueIsArray(p_src))
{
if (p_op != kMCArrayDoIntersectOpDifference)
{
r_result = MCValueRetain(kMCEmptyString);
}
else
{
r_result = MCValueRetain(p_dst);
}
return;
}
MCArrayRef t_dst_array;
t_dst_array = (MCArrayRef)p_dst;
MCArrayRef t_src_array;
t_src_array = (MCArrayRef)p_src;
MCAutoArrayRef t_result;
if (!MCArrayMutableCopy(t_dst_array, &t_result))
return;
MCNameRef t_key;
MCValueRef t_src_value;
MCValueRef t_dst_value;
uintptr_t t_iterator;
t_iterator = 0;
while(MCArrayIterate(t_dst_array, t_iterator, t_key, t_dst_value))
{
bool t_key_exists;
t_key_exists = MCArrayFetchValue(t_src_array, ctxt .
GetCaseSensitive(), t_key, t_src_value);
if (t_key_exists == (p_op == kMCArrayDoIntersectOpDifference))
{
if (!MCArrayRemoveValue(*t_result, ctxt .
GetCaseSensitive(), t_key))
{
ctxt . Throw();
return;
}
}
else if (p_op == kMCArrayDoIntersectOpIntersectRecursively)
{
MCAutoValueRef t_recursive_result;
MCArraysDoIntersect(ctxt, t_dst_value, t_src_value, p_op,
&t_recursive_result);
if (ctxt . HasError())
return;
if (!MCArrayStoreValue(*t_result, ctxt . GetCaseSensitive(),
t_key, *t_recursive_result))
return;
}
}
r_result = MCValueRetain(*t_result);
}
This is, as far as I'm aware 'bug free' - in that the code respects all
the semantics required of using all the lower-level functions it is
built out of.
Here is a version which has a vulnerability/crash in it...
static void MCArraysDoIntersect(MCExecContext& ctxt, MCValueRef p_dst,
MCValueRef p_src, MCArrayDoIntersectOp p_op, MCValueRef& r_result)
{
if (!MCValueIsArray(p_dst))
{
r_result = MCValueRetain(p_dst);
return;
}
if (!MCValueIsArray(p_src))
{
if (p_op != kMCArrayDoIntersectOpDifference)
{
r_result = MCValueRetain(kMCEmptyString);
}
else
{
r_result = p_dst;
}
return;
}
MCArrayRef t_dst_array;
t_dst_array = (MCArrayRef)p_dst;
MCArrayRef t_src_array;
t_src_array = (MCArrayRef)p_src;
MCAutoArrayRef t_result;
if (!MCArrayMutableCopy(t_dst_array, &t_result))
return;
MCNameRef t_key;
MCValueRef t_src_value;
MCValueRef t_dst_value;
uintptr_t t_iterator;
t_iterator = 0;
while(MCArrayIterate(t_dst_array, t_iterator, t_key, t_dst_value))
{
bool t_key_exists;
t_key_exists = MCArrayFetchValue(t_src_array, ctxt .
GetCaseSensitive(), t_key, t_src_value);
if (t_key_exists == (p_op == kMCArrayDoIntersectOpDifference))
{
if (!MCArrayRemoveValue(*t_result, ctxt .
GetCaseSensitive(), t_key))
{
ctxt . Throw();
return;
}
}
else if (p_op == kMCArrayDoIntersectOpIntersectRecursively)
{
MCAutoValueRef t_recursive_result;
MCArraysDoIntersect(ctxt, t_dst_value, t_src_value, p_op,
&t_recursive_result);
if (ctxt . HasError())
return;
if (!MCArrayStoreValue(*t_result, ctxt . GetCaseSensitive(),
t_key, *t_recursive_result))
return;
}
}
r_result = MCValueRetain(*t_result);
}
Can you immediately see the error?
Of course I've given you the correct and incorrect versions, so its easy
to determine by line-by-line.
However, now imagine you don't know what the correct implementation is,
and you are reviewing the code for inclusion for the first time. Would
you have noticed the error (even assuming you actually designed/wrote
most of the lower level operations which are being used here - which I
did, in this case)?
Warmest Regards,
Mark.
P.S. The above bug I introduced actually causes one of the most
pernicious sort - the effect of the bug is unlikely to be seen at the
point of effect - it will only be seen later - potentially after many
many other operations are run. For those that are interested - it
introduces a reference-count error on one of the values - which would
cause a value to be released whilst there are still references to it.
They only have an effect *when* all true references are gone, *and* the
block of memory which the value uses hasn't been re-used for the same
value type (which frequently happens because the engine uses a small
pool-allocator for values of distinct types meaning repeated freeing and
creation of the same value type has almost zero cost).
P.P.S Fortunately we have tests and tools and such which help us to find
such things - and experience in knowing where to look when reviewing.
However, it is all too easy for such 'subtle' things to be missed.
Particularly when there is a large amount of code involved.
--
Mark Waddingham ~ mark at livecode.com ~ http://www.livecode.com/
LiveCode: Everyone can create apps
More information about the use-livecode
mailing list