[Zarutian] 3. july 2007: Capicol is an variant of [picol], which in turn is an variant of [Tcl]. Capicol stands for Capability [picol] and is my investigation into capability-based security and asynchronous message passing in concurrent environment. It is not complete yet and probably very slow. (I want to get it right before fast, thank you) [Zarutian] 11. july 2007: So how does Capicol (or intend to) implement capabilities? Well, simply through a dictionary that the code running in the capicol interp doesn't have access to. That dictionary maps handles to addresses of other Capicolets (and simple i/o adaptors to the world outside the Capicolets), Capicolet being the snapshot state of an capicol interp, which might themselves be running or stored on other machines. Back to that dictionary. A Capicolet can only send an message to an address it has a handle for. So how does a Capicolet get an handle for a yet unknown address? Via the addresses field of a message that the capicol interp code replaces with handles (making new ones when coming across currently unknown addresses) upon receipt of a message and vice verse on sending. [Zarutian] 20.. july 2007: for an introduction to capability based security see http://www.skyhunter.com/marcs/capabilityIntro/index.html and on capability based security in general see erights.org New and improved version of the following code coming soon. # This code is hereby released in the public domain. # Any infriging software patents will be disregarded and # propably made invalid because of obviouseness. # v 0.1 package require Tcl 8.5 package provide capicol 0.3 # state: # running # # quota # # capabilities # #
# in-queue # * # out_queue # * # # commands # * # type # prim | combo | script # contents # returnstack # frame* # frame # pointer # # code # * # results # # # variables # # # arguments # # [break-goto] # # [continue-goto] # # [catcher] # # [save-to] # dest # # variables # # # := # := # := "beget" | "gain" | "message" # := ; for "beget" # := ; for "message" # := ; for "gain" # :=
* # := a string where [] must be replace with that result # # design decision: use primitives gain and beget or just send messages to local addresses? # hugmynd: sleppa [gain] og [beget] úr grunnskipanasafninu proc log args { if {[string equal [lindex $args 0] "invoked"]} { set call_level [lindex $args 1] puts "log: [string repeat { } $call_level] invoked [lindex $args 2 0]" } else { puts "log: $args" } } namespace eval capicol {} proc capicol::basic_tester {code} { set t1 [capicol::new_state] dict set t1 frame code $code set i [llength $code] while {$i > 0} { set t1 [capicol::advance $t1] incr i -1 } return $t1 } # tests (not complete and should be moved to the end) # capicol::basic_tester {{+ 1 2 3}} # capicol::basic_tester {{- 3 2 1}} # capicol::basic_tester {{* 5 3 2}} # capicol::basic_tester {{/ 60 5 2}} # capicol::basic_tester {{% 20 4}} # capicol::basic_tester {{& 0xFF 0x0E}} # capicol::basic_tester {{| 0xF0 0x0F}} # capicol::basic_tester {{^ 0xF0 0x0F}} # capicol::basic_tester {{<< 2 1}} # capicol::basic_tester {{>> 8 2}} # capicol::basic_tester {{< 10 5}} # capicol::basic_tester {{<= 9 10}} # capicol::basic_tester {{== 20 20}} # capicol::basic_tester {{!= 20 20}} # capicol::basic_tester {{and yes yes}} # capicol::basic_tester {{any_messages?}} proc capicol::advance {state} { log invoked [info level] [info level 0] if {![dict exists $state commands]} { error "commands missing" } if {![dict exists $state frame code]} { error "code missing" } if {![dict exists $state frame pointer]} { dict set state frame pointer 0 } if {![dict exists $state frame results]} { dict set state frame results {} } if {![dict exists $state frame variables]} { dict set state frame variables {} } if {![dict exists $state frame args]} { dict set state frame args {} } if {![dict exists $state returnstack]} { dict set state returnstack {} } set cmd&args [lindex [dict get $state frame code] [dict get $state frame pointer]] set cmd&args [interpolate [dict get $state frame results] [set cmd&args]] set cmd [lindex [set cmd&args] 0] set args [lrange [set cmd&args] 1 end] if {[dict exists $state commands $cmd]} { if {![dict exists $state commands $cmd type]} { error "type of command $cmd missing" } if {![dict exists $state commands $cmd contents]} { error "contents of command $cmd missing" } if {[string equal "combo" [dict get $state commands $cmd type]]} { # nokkuð mjög líklegt að verði mikið notað # push current continuation onto returnstack set state [push_contination $state] # stilla state fyrir að keyra innihald procs dict set state frame code [dict get $state commands $cmd contents] dict set state frame pointer -1; # þarf að vera -1 út af autoincr dict set state frame variables {} dict set state frame results {} dict set state frame args $args } elseif {[string equal "prim" [dict get $state commands $cmd type]]} { set pointer [dict get $state frame pointer] lassign [exec_prim [dict get $state commands $cmd contents] $args $state] result state dict set state frame results $pointer $result } elseif {[string equal "script" [dict get $state commands $cmd type]]} { dict set state commands $cmd script [dict get $state commands $cmd contents] dict set state commands $cmd type combo dict set state commands $cmd contents [translate [dict get $state commands $cmd contents]] return [advance $state] } else { error "unknown command type [dict get $state commands $cmd type]" } } else { # the unknown command handling if {![dict exists $state commands unknown]} { lassign [call [list error "unknown command $cmd"] $state] _ state } else { # invoke the unknown command lassign [call [list unknown [set cmd&args]] $state] temp state dict set state frame results \[[dict get $state pointer]\] $temp } } if {[llength [dict get $state frame code]] < [dict get $state frame pointer]} { set state [lindex [exec_prim_return {} $state] end] } if {([string length [dict get $state commands]] + \ [string length [dict get $state returnstack]] + \ [string length [dict get $state frame]]) > [dict get $state quota]} { lassign [call [list error "not enaugh quota to run!"] $state] _ state } dict set state frame pointer [expr [dict get $state frame pointer] + 1]; # autoincr return $state } proc capicol::push_continuation {state} { log invoked [info level] [info level 0] set temp [dict create] dict lappend state returnstack [dict get $state frame] return $state } proc capicol::call {cmd&args state} { log invoked [info level] [info level 0] set state [push_continuation $state] dict set state frame code [list [set cmd&args]] dict set state frame pointer -1 return [list {} $state] } proc capicol::exec_prim {prim arguments state} { log invoked [info level] [info level 0] # giant despatching switch # I rather use jump tables but cant have them easily in higher level languages switch -exact -- $prim { "+" { return [exec_prim_math + $arguments $state] } "-" { return [exec_prim_math - $arguments $state] } "*" { return [exec_prim_math * $arguments $state] } "/" { return [exec_prim_math / $arguments $state] } "%" { return [exec_prim_math % $arguments $state] } "&" { return [exec_prim_math & $arguments $state] } "|" { return [exec_prim_math | $arguments $state] } "^" { return [exec_prim_math ^ $arguments $state] } "<<" { return [exec_prim_math << $arguments $state] } ">>" { return [exec_prim_math >> $arguments $state] } "<" { return [exec_prim_compare < $arguments $state] } "<=" { return [exec_prim_compare <= $arguments $state] } "==" { return [exec_prim_compare == $arguments $state] } "!=" { return [exec_prim_compare != $arguments $state] } "and" { return [exec_prim_logical_and $arguments $state] } "any_messages?" { return [exec_prim_any_messages? $arguments $state] } "args" { return [exec_prim_args $arguments $state] } "beget" { return [exec_prim_beget $arguments $state] } "break" { return [exec_prim_break $arguments $state] } "catch" { return [exec_prim_catch $arguments $state] } "capabilities" { return [exec_prim_capabilites $arguments $state] } "continue" { return [exec_prim_continue $arguments $state] } "dict" { return [exec_prim_dict $arguments $state] } "die" { return [exec_prim_die $arguments $state] } "drop_capability" { return [exec_prim_drop_capability $arguments $state] } "error" { return [exec_prim_error $arguments $state] } "gain" { return [exec_prim_gain $arguments $state] } "get" { return [exec_prim_get $arguments $state] } "if" { return [exec_prim_if $arguments $state] } "lappend" { return [exec_prim_lappend $arguments $state] } "lassign" { return [exec_prim_lassign $arguments $state] } "lindex" { return [exec_prim_lindex $arguments $state] } "linsert" { return [exec_prim_linsert $arguments $state] } "list" { return [exec_prim_list $arguments $state] } "llength" { return [exec_prim_llength $arguments $state] } "lrange" { return [exec_prim_lrange $arguments $state] } "lrepeat" { return [exec_prim_lrepeat $arguments $state] } "lsearch" { return [exec_prim_lsearch $arguments $state] } "lset" { return [exec_prim_lset $arguments $state] } "lsort" { return [exec_prim_lsort $arguments $state] } "next_message" { return [exec_prim_next_message $arguments $state] } "or" { return [exec_prim_or $arguments $state] } "rename" { return [exec_prim_rename $arguments $state] } "return" { return [exec_prim_return $arguments $state] } "routine" { return [exec_prim_routine $arguments $state] } "send_message" { return [exec_prim_send_message $arguments $state] } "set" { return [exec_prim_Set $arguments $state] } "string" { return [exec_prim_string $arguments $state] } "unset" { return [exec_prim_unset $arguments $state] } "uplevel" { return [exec_prim_uplevel $arguments $state] } "var_exists?" { return [exec_prim_var_exists? $arguments $state] } "while" { return [exec_prim_while $arguments $state] } "__branch" { return [exec_prim___branch $arguments $state] } "__jump" { return [exec_prim___jump $arguments $state] } default { error "unknown capicol primitive $prim" } } } proc capicol::exec_prim_math {op arguments state} { log invoked [info level] [info level 0] set result [lindex $arguments 0] foreach item [lrange $arguments 1 end] { set result [expr $result $op $item] } return [list $result $state] } proc capicol::exec_prim_compare {op arguments state} { log invoked [info level] [info level 0] if {[llength $arguments] != 2} { return [call [list error "wrong # args: should be \"$op number number\""] $state] } set result [expr [lindex $arguments 0] $op [lindex $arguments 1]] return [list $result $state] } proc capicol::exec_prim_logical_and {arguments state} { log invoked [info level] [info level 0] set result [lindex $arguments 0] foreach item [lrange $arguments 1 end] { set result [expr $result && $item] } return [list $result $state] } proc capicol::exec_prim_any_messages? {arguments state} { log invoked [info level] [info level 0] if {![dict exists $state in_queue]} { dict set state in_queue {} } return [list [expr [llength [dict get $state in_queue]] != 0] $state] } proc capicol::exec_prim_args {arguments state} { log invoked [info level] [info level 0] if {![dict exists $state frame args]} { dict set state frame args {} } return [list [dict get $state frame args] $state] } proc capicol::exec_prim_beget {arguments state} { log invoked [info level] [info level 0] # beget returns if {[llength $arguments] != 3} { return [call [list error "wrong # args: should be \"beget startupscript capabilities quota\""] $state] } # make new address for the "child" using the replicator serial scheme if {![dict exists $state my_address]} { error "an capicol state cannot be without an address!" } if {![dict exists $state number_of_children]} { dict set state number_of_children 0 } set child "[dict get $state my_address].[dict incr state number_of_children]" if {[catch [list addresses_from_caphandles [lindex $arguments 1] $state] res]} { return [call [list error $res] $state] } else { set addresses [lindex $res 0] set state [lindex $res end] unset res } set res [add_capability $child $state] set state [lindex $res end] set handle [lindex $res 0] unset res if {[dict get $state quota] < $quota} { return [call [list error "not enaugh quota!"] $state] } if {$quota < [string length [lindex $arguments 0]]} { return [call [list error "not enaugh quota allotted to child!"] $state] } dict lappend state out_queue [list beget $child [lindex $arguments 0] $adresses $quota] return [list $handle $state] } proc capicol::add_capability {address state} { log invoked [info level] [info level 0] if {![dict exists $state capabilities counter]} { dict set state capabilities counter [expr 2 * [dict size [dict get $state capabilities]]] } if {[lsearch [dict keys $state capabilities] $address] == -1} { set handle cap[dict incr state capabilities counter] dict set state capabilities $handle $address } else { foreach {handle item} [dict get $state capabilities] { if {[string equal $item $address]} break } } return [list $handle $state] } proc capicol::addresses_from_caphandles {caphandles state} { log invoked [info level] [info level 0] set addresses {} set caphandles [dict get $state capabilities] foreach caphandle $caphandles { if {![dict exists $capabilities $caphandle]} { error "no such caphandle: $caphandle" } lappend addresses [dict get $capabilities $caphandle] } return [list $addresses $state] } proc capicol::caphandles_from_adddresses {addresses state} { log invoked [info level] [info level 0] set tmp [list] foreach item $addresses { lassign [add_capability $item $state] caphandle state lappend tmp $caphandle } return [list $tmp $state] } proc capicol::exec_prim_break {arguments state} { log invoked [info level] [info level 0] while true { if {[dict exists $state frame break-goto]} break if {[string equal [dict get $state returnstack] ""]} { return [call [list error "break invoked outside an loop"] $state] } set state [lindex [exec_prim_return {} $state] end] } dict set state frame pointer [expr [dict get $state frame break-goto] - 1] return [list {} $state] } proc capicol::exec_prim_catch {arguments state} { log invoked [info level] [info level 0] # catch