2 # class for use inside gdb which is debugging the donor process
10 def _string_bytearray(s):
11 # gets us bytes in py2 and py3
12 if not isinstance(s, bytes):
13 s = s.encode('utf-8') # sigh, python 2/3 compat
16 def _string_escape_for_c(s):
18 for c in _string_bytearray(s):
19 if c == ord('\\') or c == ord('"') or c < 32 or c > 126:
30 def _lit_aggregate_uncasted(val_lit_strs):
31 return '{' + ', '.join(['(%s)' % v for v in val_lit_strs]) + ' }'
33 def _lit_string_uncasted(s):
34 b = _string_bytearray(s)
35 return _lit_aggregate_uncasted([_lit_integer(x) for x in b] + [ '0' ])
37 def _lit_array(elemtype, val_lit_strs):
40 (elemtype, len(val_lit_strs), _lit_aggregate_uncasted(val_lit_strs))
43 def _lit_addressof(v):
44 return '&(char[])(%s)' % v
47 if isinstance(v, int):
48 return _lit_integer(v)
50 return v # should already be an integer
53 sys.stderr.write("## EVAL %s\n" % repr(expr))
54 x = gdb.parse_and_eval(expr)
55 sys.stderr.write('## => %s\n' % x)
59 class DonorStructLayout():
60 def __init__(l, typename):
61 x = parse_eval('(%s){ }' % typename)
62 l._typename = typename
65 for f in x.type.fields():
66 l._posns[f.name] = len(l._template)
67 try: f.type.fields(); blank = '{ }'
68 except TypeError: blank = '0'
69 except AttributeError: blank = '0'
70 l._template.append(blank)
71 sys.stderr.write('## STRUCT %s template %s fields %s\n'
72 % (typename, l._template, l._posns))
74 def substitute(l, values):
75 build = copy.deepcopy(l._template)
76 for (k,v) in values.items():
77 build[ l._posns[k] ] = _make_lit(v)
78 return '((%s)%s)' % (l._typename, _lit_aggregate_uncasted(build))
80 class DonorImplementation():
83 di._saved_errno = None
84 di._result_stream = os.fdopen(3, 'w')
87 # sigh, we have to record the order of the arguments!
88 def _find_fields(di, typename):
90 fields = di._structs[typename]
92 fields = DonorStructLayout(typename)
93 di._structs[typename] = fields
96 def _make(di, typename, values):
97 fields = di._find_fields(typename)
98 return fields.substitute(values)
100 # calling functions (need to cast the function name to the right
101 # type in case maybe gdb doesn't know the type)
103 def _func(di, functype, funcname, realargs):
104 expr = '((%s) %s) %s' % (functype, funcname, realargs)
105 return parse_eval(expr)
107 def _must_func(di, functype, funcname, realargs):
108 retval = di._func(functype, funcname, realargs)
110 errnoval = parse_eval('errno')
111 raise RuntimeError("%s gave errno=%d `%s'" %
112 (funcname, errnoval, os.strerror(errnoval)))
115 # wrappers for the syscalls that do what we want
117 def _sendmsg(di, carrier, control_msg):
118 iov_base = _lit_array('char', [1])
119 iov = di._make('struct iovec', {
120 'iov_base': iov_base,
124 msg = di._make('struct msghdr', {
125 'msg_iov' : _lit_addressof(iov),
127 'msg_control' : _lit_array('char', control_msg),
128 'msg_controllen': len(control_msg),
132 'ssize_t (*)(int, const struct msghdr*, int)',
134 '(%s, %s, 0)' % (carrier, _lit_addressof(msg))
138 return di._must_func(
139 'int (*)(int, int, int)',
141 '(%d, %d, 0)' % (socket.AF_UNIX, socket.SOCK_STREAM)
144 def _connect(di, fd, path):
145 addr = di._make('struct sockaddr_un', {
146 'sun_family' : _lit_integer(socket.AF_UNIX),
147 'sun_path' : _lit_string_uncasted(path),
151 'int (*)(int, const struct sockaddr*, socklen_t)',
153 '(%d, (const struct sockaddr*)%s, sizeof(struct sockaddr_un))'
154 % (fd, _lit_addressof(addr))
158 di._must_func('int (*)(int)', 'close', '(%d)' % fd)
160 def _mkdir(di, path, mode):
162 'int (*)(const char*, mode_t)',
164 '("%s", %d)' % (_string_escape_for_c(path), mode)
167 errnoval = parse_eval('errno')
168 if errnoval != os.errno.EEXIST:
169 raise RuntimeError("mkdir %s failed: `%s'" %
170 (repr(path), os.strerror(errnoval)))
175 di._saved_errno = parse_eval('errno')
177 def _errno_restore(di):
178 to_restore = di._saved_errno
179 di._saved_errno = None
180 if to_restore is not None:
181 parse_eval('errno = %d' % to_restore)
183 def _result(di, output):
184 sys.stderr.write("#> %s" % output)
185 di._result_stream.write(output)
186 di._result_stream.flush()
190 def donate(di, path, control_msg):
191 # control_msg is an array of integers being the ancillary data
192 # array ("control") for sendmsg, and hence specifies which fds
198 carrier = di._socket()
199 di._connect(carrier, path)
200 di._sendmsg(carrier, control_msg)
204 if carrier is not None:
205 try: di._close(carrier)
206 except Exception: pass
214 val = di._must_func('uid_t (*)(void)', 'geteuid', '()')
218 di._result('%d\n' % val)
223 val = di._mkdir(path, int('0700', 8))
227 di._result('%d\n' % val)
229 def _protocol_read(di):
230 input = sys.stdin.readline()
231 if input == '': return None
233 sys.stderr.write("#< %s\n" % input)
239 cmd = di._protocol_read()
240 if cmd is None: break