OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef NDEBUG | |
6 | |
7 #include "debug.h" | |
8 | |
9 namespace playground { | |
10 | |
11 bool Debug::enabled_; | |
12 int Debug::numSyscallNames_; | |
13 const char **Debug::syscallNames_; | |
14 std::map<int, std::string> Debug::syscallNamesMap_; | |
15 | |
16 Debug Debug::debug_; | |
17 | |
18 Debug::Debug() { | |
19 // Logging is disabled by default, but can be turned on by setting an | |
20 // appropriate environment variable. Initialize this code from a global | |
21 // constructor, so that it runs before the sandbox is turned on. | |
22 enabled_ = !!getenv("SECCOMP_SANDBOX_DEBUGGING"); | |
23 | |
24 // Read names of system calls from header files, if available. Symbolic | |
25 // names make debugging so much nicer. | |
26 if (enabled_) { | |
27 static const char *filenames[] = { | |
28 #if __WORDSIZE == 64 | |
29 "/usr/include/asm/unistd_64.h", | |
30 #elif __WORDSIZE == 32 | |
31 "/usr/include/asm/unistd_32.h", | |
32 #endif | |
33 "/usr/include/asm/unistd.h", | |
34 NULL }; | |
35 numSyscallNames_ = 0; | |
36 for (const char **fn = filenames; *fn; ++fn) { | |
37 FILE *fp = fopen(*fn, "r"); | |
38 if (fp) { | |
39 std::string baseName; | |
40 int baseNum = -1; | |
41 char buf[80]; | |
42 while (fgets(buf, sizeof(buf), fp)) { | |
43 // Check if the line starts with "#define" | |
44 static const char* whitespace = " \t\r\n"; | |
45 char *token, *save; | |
46 token = strtok_r(buf, whitespace, &save); | |
47 if (token && !strcmp(token, "#define")) { | |
48 | |
49 // Only parse identifiers that start with "__NR_" | |
50 token = strtok_r(NULL, whitespace, &save); | |
51 if (token) { | |
52 if (strncmp(token, "__NR_", 5)) { | |
53 continue; | |
54 } | |
55 std::string syscallName(token + 5); | |
56 | |
57 // Parse the value of the symbol. Try to be forgiving in what | |
58 // we accept, as the file format might change over time. | |
59 token = strtok_r(NULL, "\r\n", &save); | |
60 if (token) { | |
61 // Some values are defined relative to previous values, we | |
62 // detect these examples by finding an earlier symbol name | |
63 // followed by a '+' plus character. | |
64 bool isRelative = false; | |
65 char *base = strstr(token, baseName.c_str()); | |
66 if (baseNum >= 0 && base) { | |
67 base += baseName.length(); | |
68 while (*base == ' ' || *base == '\t') { | |
69 ++base; | |
70 } | |
71 if (*base == '+') { | |
72 isRelative = true; | |
73 token = base; | |
74 } | |
75 } | |
76 | |
77 // Skip any characters that are not part of the syscall number. | |
78 while (*token < '0' || *token > '9') { | |
79 token++; | |
80 } | |
81 | |
82 // If we now have a valid datum, enter it into our map. | |
83 if (*token) { | |
84 int sysnum = atoi(token); | |
85 | |
86 // Deal with symbols that are defined relative to earlier | |
87 // ones. | |
88 if (isRelative) { | |
89 sysnum += baseNum; | |
90 } else { | |
91 baseNum = sysnum; | |
92 baseName = syscallName; | |
93 } | |
94 | |
95 // Keep track of the highest syscall number that we know | |
96 // about. | |
97 if (sysnum >= numSyscallNames_) { | |
98 numSyscallNames_ = sysnum + 1; | |
99 } | |
100 | |
101 syscallNamesMap_[sysnum] = syscallName; | |
102 } | |
103 } | |
104 } | |
105 } | |
106 } | |
107 fclose(fp); | |
108 break; | |
109 } | |
110 } | |
111 if (numSyscallNames_) { | |
112 // We cannot make system calls at the time, when we are looking up | |
113 // the names. So, copy them into a data structure that can be | |
114 // accessed without having to allocated memory (i.e. no more STL). | |
115 syscallNames_ = reinterpret_cast<const char **>( | |
116 calloc(sizeof(char *), numSyscallNames_)); | |
117 for (std::map<int, std::string>::const_iterator iter = | |
118 syscallNamesMap_.begin(); | |
119 iter != syscallNamesMap_.end(); | |
120 ++iter) { | |
121 syscallNames_[iter->first] = iter->second.c_str(); | |
122 } | |
123 } | |
124 } | |
125 } | |
126 | |
127 bool Debug::enter() { | |
128 // Increment the recursion level in TLS storage. This allows us to | |
129 // make system calls from within our debugging functions, without triggering | |
130 // additional debugging output. | |
131 // | |
132 // This function can be called from both the sandboxed process and from the | |
133 // trusted process. Only the sandboxed process needs to worry about | |
134 // recursively calling system calls. The trusted process doesn't intercept | |
135 // system calls and thus doesn't have this problem. It also doesn't have | |
136 // a TLS. We explicitly set the segment selector to zero, when in the | |
137 // trusted process, so that we can avoid tracking recursion levels. | |
138 int level; | |
139 #if defined(__x86_64__) | |
140 asm volatile("mov %%gs, %0\n" | |
141 "test %0, %0\n" | |
142 "jz 1f\n" | |
143 "movl %%gs:0x1050-0xE0, %0\n" | |
144 "incl %%gs:0x1050-0xE0\n" | |
145 "1:\n" | |
146 : "=r"(level) | |
147 : | |
148 : "memory"); | |
149 #elif defined(__i386__) | |
150 asm volatile("mov %%fs, %0\n" | |
151 "test %0, %0\n" | |
152 "jz 1f\n" | |
153 "movl %%fs:0x1034-0x58, %0\n" | |
154 "incl %%fs:0x1034-0x58\n" | |
155 "1:\n" | |
156 : "=r"(level) | |
157 : | |
158 : "memory"); | |
159 #else | |
160 #error "Unsupported target platform" | |
161 #endif | |
162 return !level; | |
163 } | |
164 | |
165 bool Debug::leave() { | |
166 // Decrement the recursion level in TLS storage. This allows us to | |
167 // make system calls from within our debugging functions, without triggering | |
168 // additional debugging output. | |
169 // | |
170 // This function can be called from both the sandboxed process and from the | |
171 // trusted process. Only the sandboxed process needs to worry about | |
172 // recursively calling system calls. The trusted process doesn't intercept | |
173 // system calls and thus doesn't have this problem. It also doesn't have | |
174 // a TLS. We explicitly set the segment selector to zero, when in the | |
175 // trusted process, so that we can avoid tracking recursion levels. | |
176 int level; | |
177 #if defined(__x86_64__) | |
178 asm volatile("mov %%gs, %0\n" | |
179 "test %0, %0\n" | |
180 "jz 1f\n" | |
181 "decl %%gs:0x1050-0xE0\n" | |
182 "movl %%gs:0x1050-0xE0, %0\n" | |
183 "1:\n" | |
184 : "=r"(level) | |
185 : | |
186 : "memory"); | |
187 #elif defined(__i386__) | |
188 asm volatile("mov %%fs, %0\n" | |
189 "test %0, %0\n" | |
190 "jz 1f\n" | |
191 "decl %%fs:0x1034-0x58\n" | |
192 "movl %%fs:0x1034-0x58, %0\n" | |
193 "1:\n" | |
194 : "=r"(level) | |
195 : | |
196 : "memory"); | |
197 #else | |
198 #error Unsupported target platform | |
199 #endif | |
200 return !level; | |
201 } | |
202 | |
203 void Debug::_message(const char* msg) { | |
204 if (enabled_) { | |
205 Sandbox::SysCalls sys; | |
206 size_t len = strlen(msg); | |
207 if (len && msg[len-1] != '\n') { | |
208 // Write operations should be atomic, so that we don't interleave | |
209 // messages from multiple threads. Append a newline, if it is not | |
210 // already there. | |
211 char copy[len + 1]; | |
212 memcpy(copy, msg, len); | |
213 copy[len] = '\n'; | |
214 Sandbox::write(sys, 2, copy, len + 1); | |
215 } else { | |
216 Sandbox::write(sys, 2, msg, len); | |
217 } | |
218 } | |
219 } | |
220 | |
221 void Debug::message(const char* msg) { | |
222 if (enabled_) { | |
223 if (enter()) { | |
224 _message(msg); | |
225 } | |
226 leave(); | |
227 } | |
228 } | |
229 | |
230 void Debug::gettimeofday(long long* tm) { | |
231 if (tm) { | |
232 struct timeval tv; | |
233 #if defined(__i386__) | |
234 // Zero out the lastSyscallNum, so that we don't try to coalesce | |
235 // calls to gettimeofday(). For debugging purposes, we need the | |
236 // exact time. | |
237 asm volatile("movl $0, %fs:0x102C-0x58"); | |
238 #elif !defined(__x86_64__) | |
239 #error Unsupported target platform | |
240 #endif | |
241 ::gettimeofday(&tv, NULL); | |
242 *tm = 1000ULL*1000ULL*static_cast<unsigned>(tv.tv_sec) + | |
243 static_cast<unsigned>(tv.tv_usec); | |
244 } | |
245 } | |
246 | |
247 void Debug::syscall(long long* tm, int sysnum, const char* msg, int call) { | |
248 // This function gets called from the system call wrapper. Avoid calling | |
249 // any library functions that themselves need system calls. | |
250 if (enabled_) { | |
251 if (enter() || !tm) { | |
252 gettimeofday(tm); | |
253 | |
254 const char *sysname = NULL; | |
255 if (sysnum >= 0 && sysnum < numSyscallNames_) { | |
256 sysname = syscallNames_[sysnum]; | |
257 } | |
258 static const char kUnnamedMessage[] = "Unnamed syscall #"; | |
259 char unnamed[40]; | |
260 if (!sysname) { | |
261 memcpy(unnamed, kUnnamedMessage, sizeof(kUnnamedMessage) - 1); | |
262 itoa(unnamed + sizeof(kUnnamedMessage) - 1, sysnum); | |
263 sysname = unnamed; | |
264 } | |
265 #if defined(__NR_socketcall) || defined(__NR_ipc) | |
266 char extra[40]; | |
267 *extra = '\000'; | |
268 #if defined(__NR_socketcall) | |
269 if (sysnum == __NR_socketcall) { | |
270 static const char* socketcall_name[] = { | |
271 0, "socket", "bind", "connect", "listen", "accept", "getsockname", | |
272 "getpeername", "socketpair", "send", "recv", "sendto","recvfrom", | |
273 "shutdown", "setsockopt", "getsockopt", "sendmsg", "recvmsg", | |
274 "accept4" | |
275 }; | |
276 if (call >= 1 && | |
277 call < (int)(sizeof(socketcall_name)/sizeof(char *))) { | |
278 strcat(strcpy(extra, " "), socketcall_name[call]); | |
279 } else { | |
280 itoa(strcpy(extra, " #") + 2, call); | |
281 } | |
282 } | |
283 #endif | |
284 #if defined(__NR_ipc) | |
285 if (sysnum == __NR_ipc) { | |
286 static const char* ipc_name[] = { | |
287 0, "semop", "semget", "semctl", "semtimedop", 0, 0, 0, 0, 0, 0, | |
288 "msgsnd", "msgrcv", "msgget", "msgctl", 0, 0, 0, 0, 0, 0, | |
289 "shmat", "shmdt", "shmget", "shmctl" }; | |
290 if (call >= 1 && call < (int)(sizeof(ipc_name)/sizeof(char *)) && | |
291 ipc_name[call]) { | |
292 strcat(strcpy(extra, " "), ipc_name[call]); | |
293 } else { | |
294 itoa(strcpy(extra, " #") + 2, call); | |
295 } | |
296 } | |
297 #endif | |
298 #else | |
299 static const char extra[1] = { 0 }; | |
300 #endif | |
301 char buf[strlen(sysname) + strlen(extra) + (msg ? strlen(msg) : 0) + 4]; | |
302 strcat(strcat(strcat(strcat(strcpy(buf, sysname), extra), ": "), | |
303 msg ? msg : ""), "\n"); | |
304 _message(buf); | |
305 } | |
306 leave(); | |
307 } | |
308 } | |
309 | |
310 char* Debug::itoa(char* s, int n) { | |
311 // Remember return value | |
312 char *ret = s; | |
313 | |
314 // Insert sign for negative numbers | |
315 if (n < 0) { | |
316 *s++ = '-'; | |
317 n = -n; | |
318 } | |
319 | |
320 // Convert to decimal (in reverse order) | |
321 char *start = s; | |
322 do { | |
323 *s++ = '0' + (n % 10); | |
324 n /= 10; | |
325 } while (n); | |
326 *s-- = '\000'; | |
327 | |
328 // Reverse order of digits | |
329 while (start < s) { | |
330 char ch = *s; | |
331 *s-- = *start; | |
332 *start++ = ch; | |
333 } | |
334 | |
335 return ret; | |
336 } | |
337 | |
338 void Debug::elapsed(long long tm, int sysnum, int call) { | |
339 if (enabled_) { | |
340 if (enter()) { | |
341 // Compute the time that has passed since the system call started. | |
342 long long delta; | |
343 gettimeofday(&delta); | |
344 delta -= tm; | |
345 | |
346 // Format "Elapsed time: %d.%03dms" without using sprintf(). | |
347 char buf[80]; | |
348 itoa(strrchr(strcpy(buf, "Elapsed time: "), '\000'), delta/1000); | |
349 delta %= 1000; | |
350 strcat(buf, delta < 100 ? delta < 10 ? ".00" : ".0" : "."); | |
351 itoa(strrchr(buf, '\000'), delta); | |
352 strcat(buf, "ms"); | |
353 | |
354 // Print system call name and elapsed time. | |
355 syscall(NULL, sysnum, buf, call); | |
356 } | |
357 leave(); | |
358 } | |
359 } | |
360 | |
361 } // namespace | |
362 | |
363 #endif // NDEBUG | |
OLD | NEW |