Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> We don't need separate processes if the language we use is inherently safe (e.g., modern Common Lisp).

I note the year of the article, which was before Spectre and friends showed that this is not a reasonable assumption these days[1].

So does this mean LispOS is doomed?

[1]: https://arxiv.org/abs/1902.05178



Only doomed on broken hardware. I think that mitigating Spectre and similar attacks via software is a losing battle. Eventually hardware will have to be fixed to properly isolate microarchitecture state during speculative execution.

Also technically we still don't need separate processes to mitigate spectre, we just need to make sure that microarchitecture state is flushed when changing security domains. This is often accomplished with processes, but it could also just be accomplished by marking system methods in a LispOS as "priviledged" and decorating them to flush the microarchitecture state when called.


If you have a single address space you don't need Spectre to be able to discover any aspect of the running system.


Only if you're given raw memory access, which you are not in Lisp.


Without raw memory access, good luck supporting anything than basic hardware. Even some USB features requires it.


It's entirely possible to write a system where application code only gets sanitized handles and OS code inside the implementation gets raw memory pointers at least some of the time. All running in one address space doesn't mean all running in one security context, necessarily.

Really, address spaces are just a hardware implementation of a more general concept: Namespace-based security. Application code wouldn't even be able to know the names of privileged objects, and if someone told that code the right names, it wouldn't be able to use them, because resolution of names to things is, itself, privileged.

In a simple example, assuming a Common Lisp-like system: Everything which handles raw pointers is in the SYSTEM package. Application code can't inspect the SYSTEM package, and, even if you told an application that the function SYSTEM:WRITE-DATA-TO-DISK was a thing, it couldn't call that function because the real evaluation code, which can see into the SYSTEM namespace, knows not to let application code call anything in SYSTEM; only the functions in SYSTEM and SYSCALL can do that.


You were given raw memory access on historical Lisp Machines.


"Lisp" is a family of languages; what you're given in a family of languages is ... languages. What we find under a language is one or more implementations, and what you're given in some implementations is various forms of raw memory access.

TXR Lisp: let's gain access to malloc, allocate 42 bytes and then pretend that this block contains 100 "uint32" words (400 bytes):

  This is the TXR Lisp interactive listener of TXR 215.
  Quit with :quit or Ctrl-D on empty line. Ctrl-X ? for cheatsheet.
  1> (with-dyn-lib nil (deffi malloc "malloc" cptr (size-t)))
  #:lib-0010
  2> (malloc 42)
  #<cptr: 9158a18>
  3> (carray-cptr *2 (ffi uint32) 100)
  #<carray 100 #<ffi-type uint32>>
  4> (vec-carray *3)
  #(8381552 8381552 0 0 152124648 152288384 152288632 152288800 152288952
    152289168 152289336 121 0 60 10 0 16 0 0 0 128 16 0 0 0 152292000
    0 0 0 152126920 151513152 152292584 152127032 152292680 152292896
    152293096 152293240 152293368 0 0 152293752 73 35 60 99 112 116
    114 58 32 57 49 53 56 97 49 56 62 0 73 40 118 101 99 45 99 97
    114 114 97 121 32 42 51 41 0 72 57 40 109 97 108 108 111 99 32
    52 50 41 0 152304256 121 0 60 10 0 3 0 0 0)
TXR Lisp has good sandboxing for applications. The data structure which maps symbol package names to packages is itself a variable that can be overridden. So we can create a sandbox in which the sys package doesn't exist, and the usr package prefix refers to an altered one that has been scrubbed to a safe subset of symbols.

http://nongnu.org/txr/txr-manpage.html#N-00E20381

The sys:bits function is available now:

  1> (sys:bits (cons 1 2))
  3076696060

  2> *package-alist*
  (("pub" . #<package: pub>) ("usr" . #<package: usr>) ("keyword" . #<package: keyword>)
   ("sys" . #<package: sys>))
Remove the "sys" package:

  3> [remqual "sys" *package-alist* car]
  (("pub" . #<package: pub>) ("usr" . #<package: usr>) ("keyword" . #<package: keyword>))
  4> (set *package-alist* *3)
  (("pub" . #<package: pub>) ("usr" . #<package: usr>) ("keyword" . #<package: keyword>))
The symbol sys:bits is not recognized any more:

  5> (sys:bits (cons 1 2))
  expr-5:1: sys:bits: package sys not found
  ** syntax error
Because package-alist is a special variable, it's possible to dynamically override with let rather than globally clobber it in the above manner. The dynamic environment can then be a sandbox.


How would you then circumvent the protection?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: