[pro] Heartbleed?

Pascal J. Bourguignon pjb at informatimago.com
Sun Apr 13 10:01:01 UTC 2014


David McClain <dbm at refined-audiometrics.com> writes:

> Just curious for other opinions... but wouldn't this (Heartbleed)
> sort of buffer excess read-back failure have been prevented by
> utilizing a "safe" language like Lisp or SML?
>
> I used to be an "unsafe" language bigot -- having mastered C/C++ for
> many years, and actually producing C compilers for a living at one
> time. I felt there should be no barriers to me as master of my
> machine, and not the other way around.

Oh, so you are directly (if partially) responsible for the C mess!


The C standards say that:

   { char a[10]; return a[12]; } 

is _undefined_.

Why, as a compiler writer, didn't you define it to raise an exception?
Yes, the C standard doesn't define exceptions, why, as a compiler
writer, didn't you add this obvious extension?




Notice how CLHS aref specifies:

    subscripts---a list of valid array indices for the array.

    Exceptional Situations: None.

and how:

    1.4.4.3 The ``Arguments and Values'' Section of a Dictionary Entry

    An English language description of what arguments the operator
    accepts and what values it returns, including information about
    defaults for parameters corresponding to omittable arguments (such
    as optional parameters and keyword parameters). For special
    operators and macros, their arguments are not evaluated unless it is
    explicitly stated in their descriptions that they are evaluated.

    Except as explicitly specified otherwise, the consequences are
    undefined if these type restrictions are violated.




Which means that (let ((a (make-array 10))) (aref a 12)) is as undefined
in CL as in C!


However, you don't see Lisp implementers allow it, and instead they all
signal an error:

[pjb at kuiper :0.0 tmp]$ clall -r '(let ((a (make-array 10))) (aref a 12))'

Armed Bear Common Lisp         Invalid array index 12 for #(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (should be >= 0 and < 10).
Clozure Common Lisp            Array index 12 out of bounds for #(0 0 0 0 0 0 0 0 0 0) .
CLISP                          AREF: index 12 for #(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) is out of range 
CMU Common Lisp                 Error in function LISP::%ARRAY-ROW-MAJOR-INDEX:    Invalid index 12 in #(0 0 0 0 0 0 0 0 0 0)
ECL                            In function AREF, the index into the object  #(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL). takes a value 12 out of the range (INTEGER 0 9).
SBCL                           Value of SB-INT:INDEX in   (THE (INTEGER 0 (10)) SB-INT:INDEX) is   12, not a   (MOD 10).




And even with safety 0, which should never be used, (but perhaps on a
specific function that you've proven needs to 2 cycles faster, for which
you can't find a better algorithm, and when you have proven and tested
that bounds and other adverse conditions couldn't occur, that is, so
many conditions that they never occur in real life), non-toy
implementations still check bounds:

[pjb at kuiper :0.0 tmp]$ clall -r '(declaim (optimize (safety 0) (speed 3) (debug 0) (space 3)))' '(let ((a (make-array 10))) (aref a 12))'

Armed Bear Common Lisp         --> NIL
Armed Bear Common Lisp         Invalid array index 12 for #(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (should be >= 0 and < 10).
CCL
/home/pjb/bin/clall: line 284: 16162 Segmentation fault      "$implementation" "$@" "${user_args[@]}" > "$error" 2>&1
CLISP                          --> NIL
CLISP                          AREF: index 12 for #(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) is out of range 
CMU Common Lisp                --> NIL
CMU Common Lisp                 Error in function LISP::%ARRAY-ROW-MAJOR-INDEX:    Invalid index 12 in #(0 0 0 0 0 0 0 0 0 0)
ECL                            --> NIL
ECL                            In function AREF, the index into the object  #(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL). takes a value 12 out of the range (INTEGER 0 9).
SBCL                           --> No value.
SBCL                           --> 0






Here is how a lisp programmer implements a C compiler:

cl-user> (ql:quickload :vacietis)
;; …
cl-user> (defun read-c-expression-from-string (source)
           (let ((*readtable*               vacietis:c-readtable)
                 (vacietis:*compiler-state* (vacietis:make-compiler-state)))
             (read-from-string source)))
read-c-expression-from-string
cl-user> (read-c-expression-from-string "char f(){ char a[10]; return a[12]; }")
(vacietis::defun/1 f nil (prog* ((a (vacietis:allocate-memory 10))) (return (vacietis.c:[] a 12))))
cl-user> (eval (read-c-expression-from-string "char f(){ char a[10]; return a[12]; }"))
f
cl-user> (f)
> Debug: Array index 12 out of bounds for #<vector 10, adjustable> .
> While executing: (:internal swank::invoke-default-debugger), in process repl-thread(871).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.
1 > :q
; Evaluation aborted on #<simple-error #x302003010B7D>.
cl-user> 




A final word:  Don't use FFI!  Implement the libraries you need in Lisp!
-- 
__Pascal Bourguignon__
http://www.informatimago.com/
"Le mercure monte ?  C'est le moment d'acheter !"




More information about the pro mailing list