The code/data rhetoric is not exclusively an earmark of Lisp. A primary concern in security are situations in which what is intended to be data allows for a surprising malicious use whereby it effectively becomes a way to program behaviors into the software which processes the data.
For instance, Winamp being exploited by loading a specially crafted MP3 playlist file is an instance of data becoming code; no Lisp in sight. Code being a flipside of data, in the context of security, is simply the consequence of the Von Neumann model.
Graham is arguing for compartments; exactly why we use containers and such. The main idea is that the passenger cabin should be sandboxed away from the cockpit, which isn't earth-shattering.
Where he mentions Lisp is this:
> The defense that does work is to keep code and data in separate places. Then there is no way to compromise code by playing tricks with data. Garbage-collected languages like Perl and Lisp do this, and as a result are immune from buffer overflow attacks.
This point seems bungled. Garbage collection is no panacea. Bugs in a garbage collected run-time can expose applications to exploitable attacks. The advantage in Perl and Lisp is that the application programmer is not writing low-level buffer manipulation routines from scratch. That stuff is in the language run time, where we debug the low-level implementation code once, and have thousands of applications use the debugged thing. If a hole is found, we fix it once, and close the issue in thousands of apps once they upgrade to the new run-time.
Garbage collected languages do not necessarily keep code and data in separate places. Moreover, non-garbage-collected languages do that. The C language can easily be implemented in a conforming manner such that the data areas (automatic storage (a.k.a. stack) and dynamic (malloc) are not executable. C keeps code and data separate. Functions are not objects. In some C implementations, like certain DSP chips, functions are in a separate space; you cannot cast a function pointer to char * and access the function image, because the resulting pointer will be interpreted in a different memory area. Of course, C data can contain pointers to functions, which can be redirected if overwritten.
Lisp data can also contain function pointers: e.g. a closure object with a pointer to code and to an environment. If that is stored in a heap somewhere where an array is also stored, and the run-time has a bug which allows array overrun, it could perhaps be exploited.