source: trunk/base/src/port1.0/porttrace.tcl @ 29641

Last change on this file since 29641 was 29641, checked in by epimenov@…, 13 years ago

Adding portpath, /etc/passwd and /etc/localtime into trace sandbox

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.6 KB
Line 
1# et:ts=4
2# porttrace.tcl
3#
4# $Id: porttrace.tcl 29641 2007-10-04 12:44:04Z epimenov@macports.org $
5#
6# Copyright (c) 2005-2006 Paul Guyot <pguyot@kallisys.net>,
7# All rights reserved.
8#
9# Redistribution and use in source and binary forms, with or without
10# modification, are permitted provided that the following conditions are
11# met:
12#
13# 1. Redistributions of source code must retain the above copyright
14#    notice, this list of conditions and the following disclaimer.
15# 2. Redistributions in binary form must reproduce the above copyright
16#    notice, this list of conditions and the following disclaimer in the
17#    documentation and/or other materials provided with the distribution.
18# 3. Neither the name of Apple Computer, Inc. nor the names of its
19#    contributors may be used to endorse or promote products derived from
20#    this software without specific prior written permission.
21#
22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33#
34
35package provide porttrace 1.0
36package require Pextlib 1.0
37
38proc trace_start {workpath} {
39        global os.platform
40        if {${os.platform} == "darwin"} {
41                if {[catch {package require Thread} error]} {
42                        ui_warn "trace requires Tcl Thread package ($error)"
43                } else {
44                        global env trace_fifo trace_sandboxbounds portpath
45                        # Create a fifo.
46                        # path in unix socket limited to 109 chars
47                        # # set trace_fifo "$workpath/trace_fifo"
48                        set trace_fifo "/tmp/macports/[pid]_[expr {int(rand()*1000)}]" 
49                        file mkdir "/tmp/macports"
50                        file delete -force $trace_fifo
51                       
52                        # Create the thread/process.
53                        create_slave $workpath $trace_fifo
54                                       
55                        # Launch darwintrace.dylib.
56                       
57                        set tracelib_path [file join ${portutil::autoconf::prefix} share macports Tcl darwintrace1.0 darwintrace.dylib]
58
59                        if {[info exists env(DYLD_INSERT_LIBRARIES)]} {
60                                set env(DYLD_INSERT_LIBRARIES) "${env(DYLD_INSERT_LIBRARIES)}:${tracelib_path}"
61                        } else {
62                                set env(DYLD_INSERT_LIBRARIES) ${tracelib_path}
63                        }
64                        set env(DYLD_FORCE_FLAT_NAMESPACE) 1
65                        set env(DARWINTRACE_LOG) "$trace_fifo"
66                        # The sandbox is limited to:
67                        # workpath
68                        # /tmp
69                        # /private/tmp
70                        # /var/tmp
71                        # /private/var/tmp
72                        # $TMPDIR
73                        # /dev/null
74                        # /dev/tty
75                        # /Library/Caches/com.apple.Xcode
76                        set trace_sandboxbounds "/tmp:/private/tmp:/var/tmp:/private/var/tmp:/dev/:/etc/passwd:/etc/groups:/etc/localtime:/Library/Caches/com.apple.Xcode:${workpath}:$portpath"
77                        if {[info exists env(TMPDIR)]} {
78                                set trace_sandboxbounds "${trace_sandboxbounds}:$env(TMPDIR)"
79                        }
80                        tracelib setsandbox $trace_sandboxbounds
81                }
82        }
83}
84
85# Enable the fence.
86# Only done for targets that should only happen in the sandbox.
87proc trace_enable_fence {} {
88        global env trace_sandboxbounds
89        set env(DARWINTRACE_SANDBOX_BOUNDS) $trace_sandboxbounds
90        tracelib enablefence
91}
92
93# Disable the fence.
94# Unused yet.
95proc trace_disable_fence {} {
96        global env
97        if [info exists env(DARWINTRACE_SANDBOX_BOUNDS)] {
98                unset env(DARWINTRACE_SANDBOX_BOUNDS)
99        }
100}
101
102# Check the list of ports.
103# Output a warning for every port the trace revealed a dependency on
104# that isn't included in portslist
105# This method must be called after trace_start
106proc trace_check_deps {target portslist} {
107        # Get the list of ports.
108        set ports [slave_send slave_get_ports]
109       
110        # Compare with portslist
111        set portslist [lsort $portslist]
112        foreach port $ports {
113                if {[lsearch -sorted -exact $portslist $port] == -1} {
114                        ui_warn "Target $target has an undeclared dependency on $port"
115                }
116        }
117        foreach port $portslist {
118                if {[lsearch -sorted -exact $ports $port] == -1} {
119                        ui_debug "Target $target has no traceable dependency on $port"
120                }
121        }       
122}
123
124# Check that no violation happened.
125# Output a warning for every sandbox violation the trace revealed.
126# This method must be called after trace_start
127proc trace_check_violations {} {
128        # Get the list of violations.
129        set violations [slave_send slave_get_sandbox_violations]
130       
131        foreach violation [lsort $violations] {
132                ui_warn "A creation/deletion/modification was attempted outside sandbox: $violation"
133        }
134}
135
136# Stop the trace and return the list of ports the port depends on.
137# This method must be called after trace_start
138proc trace_stop {} {
139        global os.platform
140        if {${os.platform} == "darwin"} {
141                global env trace_fifo
142                unset env(DYLD_INSERT_LIBRARIES)
143                unset env(DYLD_FORCE_FLAT_NAMESPACE)
144                unset env(DARWINTRACE_LOG)
145                if [info exists env(DARWINTRACE_SANDBOX_BOUNDS)] {
146                        unset env(DARWINTRACE_SANDBOX_BOUNDS)
147                }
148               
149                #kill socket
150                tracelib clean
151
152                # Clean up.
153                slave_send slave_stop
154
155                # Delete the slave.
156                delete_slave
157
158                file delete -force $trace_fifo
159        }
160}
161
162# Private
163# Create the slave thread.
164proc create_slave {workpath trace_fifo} {
165        global trace_thread
166        # Create the thread.
167        set trace_thread [macports_create_thread]
168       
169        # The slave thread requires the registry package.
170        thread::send -async $trace_thread "package require registry 1.0"
171        # and this file as well.
172        thread::send -async $trace_thread "package require porttrace 1.0"
173
174        # Start the slave work.
175        thread::send -async $trace_thread "slave_start $trace_fifo $workpath"
176}
177
178# Private
179# Send a command to the thread without waiting for the result.
180proc slave_send_async {command} {
181        global trace_thread
182
183        thread::send -async $trace_thread "$command"
184}
185
186# Private
187# Send a command to the thread.
188proc slave_send {command} {
189        global trace_thread
190
191        # ui_warn "slave send $command ?"
192
193        thread::send $trace_thread "$command" result
194        return $result
195}
196
197# Private
198# Destroy the thread.
199proc delete_slave {} {
200        global trace_thread
201
202        # Destroy the thread.
203        thread::release $trace_thread
204}
205
206# Private.
207# Slave method to read a line from the trace.
208proc slave_read_line {chan} {
209        global ports_list trace_filemap sandbox_violation_list workpath
210        global env
211
212        while 1 {
213                # We should never get EOF, actually.
214                if {[eof $chan]} {
215                        break
216                }
217               
218                # The line is of the form: verb\tpath
219                # Get the path by chopping it.
220                set theline [gets $chan]
221               
222                if {[fblocked $chan]} {
223                        # Exit the loop.
224                        break
225                }
226
227                set line_length [string length $theline]
228               
229                # Skip empty lines.
230                if {$line_length > 0} {
231                        set path_start [expr [string first "\t" $theline] + 1]
232                        set op [string range $theline 0 [expr $path_start - 2]]
233                        set path [string range $theline $path_start [expr $line_length - 1]]
234                       
235                        # open/execve
236                        if {$op == "open" || $op == "execve"} {
237                                # Only work on files.
238                                if {[file isfile $path]} {
239                                        # Did we process the file yet?
240                                        if {![filemap exists trace_filemap $path]} {
241                                                # Obtain information about this file.
242                                                set port [registry::file_registered $path]
243                                                if { $port != 0 } {
244                                                        # Add the port to the list.
245                                                        if {[lsearch -sorted -exact $ports_list $port] == -1} {
246                                                                lappend ports_list $port
247                                                                set ports_list [lsort $ports_list]
248                                                                # Maybe fill trace_filemap for efficiency?
249                                                        }
250                                                }
251                       
252                                                # Add the file to the tree with port information.
253                                                # Ignore errors. Errors can occur if a directory was
254                                                # created where a file once lived.
255                                                # This doesn't affect existing ports and we just
256                                                # add this information to speed up port detection.
257                                                catch {filemap set trace_filemap $path $port}
258                                        }
259                                }
260                        } elseif {$op == "sandbox_violation"} {
261                                lappend sandbox_violation_list $path
262                        }
263                }
264        }
265}
266
267# Private.
268# Slave init method.
269proc slave_start {fifo p_workpath} {
270        global ports_list trace_filemap sandbox_violation_list
271        # Save the workpath.
272        set workpath $p_workpath
273        # Create a virtual filemap.
274        filemap create trace_filemap
275        set ports_list {}
276        set sandbox_violation_list {}
277        tracelib setname $fifo
278        tracelib run
279}
280
281# Private.
282# Slave cleanup method.
283proc slave_stop {} {
284        global trace_filemap trace_fifo_r_chan trace_fifo_w_chan
285        # Close the virtual filemap.
286        filemap close trace_filemap
287        # Close the pipe (both ends).
288}
289
290# Private.
291# Slave ports export method.
292proc slave_get_ports {} {
293        global ports_list
294        return $ports_list
295}
296
297# Private.
298# Slave sandbox violations export method.
299proc slave_get_sandbox_violations {} {
300        global sandbox_violation_list
301        return $sandbox_violation_list
302}
303
304proc slave_add_sandbox_violation {path} {
305        global sandbox_violation_list
306        lappend sandbox_violation_list $path
307}
Note: See TracBrowser for help on using the repository browser.