[threads] Allow resetting the name of a threadpool thread (#4350)
[mono.git] / mono / metadata / attach.c
1 /*
2  * attach.c: Support for attaching to the runtime from other processes.
3  *
4  * Author:
5  *   Zoltan Varga (vargaz@gmail.com)
6  *
7  * Copyright 2007-2009 Novell, Inc (http://www.novell.com)
8  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9  */
10
11 #include <config.h>
12 #include <glib.h>
13
14 #ifdef HOST_WIN32
15 #define DISABLE_ATTACH
16 #endif
17 #ifndef DISABLE_ATTACH
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 #include <sys/un.h>
26 #include <netinet/in.h>
27 #include <fcntl.h>
28 #include <inttypes.h>
29 #include <pwd.h>
30 #include <errno.h>
31 #include <netdb.h>
32 #include <unistd.h>
33
34 #include <mono/metadata/assembly.h>
35 #include <mono/metadata/metadata.h>
36 #include <mono/metadata/class-internals.h>
37 #include <mono/metadata/object-internals.h>
38 #include <mono/metadata/threads-types.h>
39 #include <mono/metadata/gc-internals.h>
40 #include <mono/utils/mono-threads.h>
41 #include "attach.h"
42
43 #include <mono/utils/w32api.h>
44
45 /*
46  * This module enables other processes to attach to a running mono process and
47  * load agent assemblies. 
48  * Communication is done through a UNIX Domain Socket located at
49  * /tmp/mono-<USER>/.mono-<PID>.
50  * We use a simplified version of the .net remoting protocol.
51  * To increase security, and to avoid spinning up a listener thread on startup,
52  * we follow the java implementation, and only start up the attach mechanism
53  * when we receive a QUIT signal and there is a file named 
54  * '.mono_attach_pid<PID>' in /tmp.
55  *
56  * SECURITY:
57  * - This module allows loading of arbitrary code into a running mono runtime, so
58  *   it is security critical.
59  * - Security is based on controlling access to the unix file to which the unix 
60  *   domain socket is bound. Permissions/ownership are set such that only the owner 
61  *   of the process can access the socket.
62  * - As an additional measure, the socket is only created when the process receives
63  *   a SIGQUIT signal, which only its owner/root can send.
64  * - The socket is kept in a directory whose ownership is checked before creating
65  *   the socket. This could allow an attacker a kind of DOS attack by creating the 
66  *   directory with the wrong permissions/ownership. However, the only thing such
67  *   an attacker could prevent is the attaching of agents to the mono runtime.
68  */
69
70 typedef struct {
71         gboolean enabled;
72 } AgentConfig;
73
74 typedef struct {
75         int bytes_sent;
76 } AgentStats;
77
78 /*******************************************************************/
79 /* Remoting Protocol type definitions from [MS-NRBF] and [MS-NRTP] */
80 /*******************************************************************/
81
82 typedef enum {
83         PRIM_TYPE_INT32 = 8,
84         PRIM_TYPE_INT64 = 9,
85         PRIM_TYPE_NULL = 17,
86         PRIM_TYPE_STRING = 18
87 } PrimitiveType;
88
89 static AgentConfig config;
90
91 static int listen_fd, conn_fd;
92
93 static char *ipc_filename;
94
95 static char *server_uri;
96
97 static MonoThreadHandle *receiver_thread_handle;
98
99 static gboolean stop_receiver_thread;
100
101 static gboolean needs_to_start, started;
102
103 static void transport_connect (void);
104
105 static gsize WINAPI receiver_thread (void *arg);
106
107 static void transport_start_receive (void);
108
109 /*
110  * Functions to decode protocol data
111  */
112 static inline int
113 decode_byte (guint8 *buf, guint8 **endbuf, guint8 *limit)
114 {
115         *endbuf = buf + 1;
116         g_assert (*endbuf <= limit);
117         return buf [0];
118 }
119
120 static inline int
121 decode_int (guint8 *buf, guint8 **endbuf, guint8 *limit)
122 {
123         *endbuf = buf + 4;
124         g_assert (*endbuf <= limit);
125
126         return (((int)buf [0]) << 0) | (((int)buf [1]) << 8) | (((int)buf [2]) << 16) | (((int)buf [3]) << 24);
127 }
128
129 static char*
130 decode_string_value (guint8 *buf, guint8 **endbuf, guint8 *limit)
131 {
132         int type;
133     gint32 length;
134         guint8 *p = buf;
135         char *s;
136
137         type = decode_byte (p, &p, limit);
138         if (type == PRIM_TYPE_NULL) {
139                 *endbuf = p;
140                 return NULL;
141         }
142         g_assert (type == PRIM_TYPE_STRING);
143
144         length = 0;
145         while (TRUE) {
146                 guint8 b = decode_byte (p, &p, limit);
147                 
148                 length <<= 8;
149                 length += b;
150                 if (b <= 0x7f)
151                         break;
152         }
153
154         g_assert (length < (1 << 16));
155
156         s = (char *)g_malloc (length + 1);
157
158         g_assert (p + length <= limit);
159         memcpy (s, p, length);
160         s [length] = '\0';
161         p += length;
162
163         *endbuf = p;
164
165         return s;
166 }
167
168 /********************************/
169 /*    AGENT IMPLEMENTATION      */
170 /********************************/
171
172 void
173 mono_attach_parse_options (char *options)
174 {
175         if (!options)
176                 return;
177         if (!strcmp (options, "disable"))
178                 config.enabled = FALSE;
179 }
180
181 void
182 mono_attach_init (void)
183 {
184         config.enabled = TRUE;
185 }
186
187 /**
188  * mono_attach_start:
189  *
190  * Start the attach mechanism if needed.  This is called from a signal handler so it must be signal safe.
191  *
192  * Returns: whenever it was started.
193  */
194 gboolean
195 mono_attach_start (void)
196 {
197         char path [256];
198         int fd;
199
200         if (started)
201                 return FALSE;
202
203         /* Check for the existence of the trigger file */
204
205         /* 
206          * We don't do anything with this file, and the only thing an attacker can do
207          * by creating it is to enable the attach mechanism if the process receives a 
208          * SIGQUIT signal, which can only be sent by the owner/root.
209          */
210         snprintf (path, sizeof (path), "/tmp/.mono_attach_pid%"PRIdMAX"", (intmax_t) getpid ());
211         fd = open (path, O_RDONLY);
212         if (fd == -1)
213                 return FALSE;
214         close (fd);
215
216         if (!config.enabled)
217                 /* Act like we started */
218                 return TRUE;
219
220         if (started)
221                 return FALSE;
222
223         /*
224          * Our startup includes non signal-safe code, so ask the finalizer thread to 
225          * do the actual startup.
226          */
227         needs_to_start = TRUE;
228         mono_gc_finalize_notify ();
229
230         return TRUE;
231 }
232
233 /* Called by the finalizer thread when it is woken up */
234 void
235 mono_attach_maybe_start (void)
236 {
237         if (!needs_to_start)
238                 return;
239
240         needs_to_start = FALSE;
241         if (!started) {
242                 transport_start_receive ();
243
244                 started = TRUE;
245         }
246 }
247
248 void
249 mono_attach_cleanup (void)
250 {
251         if (listen_fd)
252                 close (listen_fd);
253         if (ipc_filename)
254                 unlink (ipc_filename);
255
256         stop_receiver_thread = TRUE;
257         if (conn_fd)
258                 /* This will cause receiver_thread () to break out of the read () call */
259                 close (conn_fd);
260
261         /* Wait for the receiver thread to exit */
262         if (receiver_thread_handle)
263                 mono_thread_info_wait_one_handle (receiver_thread_handle, 0, FALSE);
264 }
265
266 static int
267 mono_attach_load_agent (MonoDomain *domain, char *agent, char *args, MonoObject **exc)
268 {
269         MonoError error;
270         MonoAssembly *agent_assembly;
271         MonoImage *image;
272         MonoMethod *method;
273         guint32 entry;
274         MonoArray *main_args;
275         gpointer pa [1];
276         MonoImageOpenStatus open_status;
277
278         agent_assembly = mono_assembly_open (agent, &open_status);
279         if (!agent_assembly) {
280                 fprintf (stderr, "Cannot open agent assembly '%s': %s.\n", agent, mono_image_strerror (open_status));
281                 g_free (agent);
282                 return 2;
283         }
284
285         /* 
286          * Can't use mono_jit_exec (), as it sets things which might confuse the
287          * real Main method.
288          */
289         image = mono_assembly_get_image (agent_assembly);
290         entry = mono_image_get_entry_point (image);
291         if (!entry) {
292                 g_print ("Assembly '%s' doesn't have an entry point.\n", mono_image_get_filename (image));
293                 g_free (agent);
294                 return 1;
295         }
296
297         method = mono_get_method_checked (image, entry, NULL, NULL, &error);
298         if (method == NULL){
299                 g_print ("The entry point method of assembly '%s' could not be loaded due to %s\n", agent, mono_error_get_message (&error));
300                 mono_error_cleanup (&error);
301                 g_free (agent);
302                 return 1;
303         }
304         
305         
306         main_args = (MonoArray*)mono_array_new_checked (domain, mono_defaults.string_class, (args == NULL) ? 0 : 1, &error);
307         if (main_args == NULL) {
308                 g_print ("Could not allocate main method args due to %s\n", mono_error_get_message (&error));
309                 mono_error_cleanup (&error);
310                 g_free (agent);
311                 return 1;
312         }
313
314         if (args) {
315                 mono_array_set (main_args, MonoString*, 0, mono_string_new (domain, args));
316         }
317
318
319         pa [0] = main_args;
320         mono_runtime_try_invoke (method, NULL, pa, exc, &error);
321         if (!is_ok (&error)) {
322                 g_print ("The entry point method of assembly '%s' could not be executed due to %s\n", agent, mono_error_get_message (&error));
323                 mono_error_cleanup (&error);
324                 g_free (agent);
325                 return 1;
326         }
327
328         g_free (agent);
329
330         return 0;
331 }
332
333 /*
334  * ipc_connect:
335  *
336  *   Create a UNIX domain socket and bind it to a file in /tmp.
337  *
338  * SECURITY: This routine is _very_ security critical since we depend on the UNIX
339  * permissions system to prevent attackers from connecting to the socket.
340  */
341 static void
342 ipc_connect (void)
343 {
344         struct sockaddr_un name;
345         int sock, res;
346         size_t size;
347         char *filename, *directory;
348         struct stat stat;
349         struct passwd pwbuf;
350         char buf [1024];
351         struct passwd *pw;
352
353         if (getuid () != geteuid ()) {
354                 fprintf (stderr, "attach: disabled listening on an IPC socket when running in setuid mode.\n");
355                 return;
356         }
357
358         /* Create the socket.   */  
359         sock = socket (PF_UNIX, SOCK_STREAM, 0);
360         if (sock < 0) {
361                 perror ("attach: failed to create IPC socket");
362                 return;
363         }
364
365         /* 
366          * For security reasons, create a directory to hold the listening socket,
367          * since there is a race between bind () and chmod () below.
368          */
369         /* FIXME: Use TMP ? */
370         pw = NULL;
371 #ifdef HAVE_GETPWUID_R
372         res = getpwuid_r (getuid (), &pwbuf, buf, sizeof (buf), &pw);
373 #else
374         pw = getpwuid(getuid ());
375         res = pw != NULL ? 0 : 1;
376 #endif
377         if (res != 0) {
378                 fprintf (stderr, "attach: getpwuid_r () failed.\n");
379                 return;
380         }
381         g_assert (pw);
382         directory = g_strdup_printf ("/tmp/mono-%s", pw->pw_name);
383         res = mkdir (directory, S_IRUSR | S_IWUSR | S_IXUSR);
384         if (res != 0) {
385                 if (errno == EEXIST) {
386                         /* Check type and permissions */
387                         res = lstat (directory, &stat);
388                         if (res != 0) {
389                                 perror ("attach: lstat () failed");
390                                 return;
391                         }
392                         if (!S_ISDIR (stat.st_mode)) {
393                                 fprintf (stderr, "attach: path '%s' is not a directory.\n", directory);
394                                 return;
395                         }
396                         if (stat.st_uid != getuid ()) {
397                                 fprintf (stderr, "attach: directory '%s' is not owned by the current user.\n", directory);
398                                 return;
399                         }
400                         if ((stat.st_mode & S_IRWXG) != 0 || (stat.st_mode & S_IRWXO) || ((stat.st_mode & S_IRWXU) != (S_IRUSR | S_IWUSR | S_IXUSR))) {
401                                 fprintf (stderr, "attach: directory '%s' should have protection 0700.\n", directory);
402                                 return;
403                         }
404                 } else {
405                         perror ("attach: mkdir () failed");
406                         return;
407                 }
408         }
409
410         filename = g_strdup_printf ("%s/.mono-%"PRIdMAX"", directory, (intmax_t) getpid ());
411         unlink (filename);
412
413         /* Bind a name to the socket.   */
414         name.sun_family = AF_UNIX;
415         strcpy (name.sun_path, filename);
416
417         size = (offsetof (struct sockaddr_un, sun_path)
418                         + strlen (name.sun_path) + 1);
419
420         if (bind (sock, (struct sockaddr *) &name, size) < 0) {
421                 fprintf (stderr, "attach: failed to bind IPC socket '%s': %s\n", filename, strerror (errno));
422                 close (sock);
423                 return;
424         }
425
426         /* Set permissions */
427         res = chmod (filename, S_IRUSR | S_IWUSR);
428         if (res != 0) {
429                 perror ("attach: failed to set permissions on IPC socket");
430                 close (sock);
431                 unlink (filename);
432                 return;
433         }
434
435         res = listen (sock, 16);
436         if (res != 0) {
437                 fprintf (stderr, "attach: listen () failed: %s\n", strerror (errno));
438                 exit (1);
439         }
440
441         listen_fd = sock;
442
443         ipc_filename = g_strdup (filename);
444
445         server_uri = g_strdup_printf ("unix://%s/.mono-%"PRIdMAX"?/vm", directory, (intmax_t) getpid ());
446
447         g_free (filename);
448         g_free (directory);
449 }
450
451 static void
452 transport_connect (void)
453 {
454         ipc_connect ();
455 }
456
457 #if 0
458
459 static void
460 transport_send (int fd, guint8 *data, int len)
461 {
462         int res;
463
464         stats.bytes_sent += len;
465         //printf ("X: %d\n", stats.bytes_sent);
466
467         res = write (fd, data, len);
468         if (res != len) {
469                 /* FIXME: What to do here ? */
470         }
471 }
472
473 #endif
474
475 static void
476 transport_start_receive (void)
477 {
478         transport_connect ();
479
480         if (!listen_fd)
481                 return;
482
483         receiver_thread_handle = mono_threads_create_thread (receiver_thread, NULL, NULL, NULL);
484         g_assert (receiver_thread_handle);
485 }
486
487 static gsize WINAPI
488 receiver_thread (void *arg)
489 {
490         MonoError error;
491         int res, content_len;
492         guint8 buffer [256];
493         guint8 *p, *p_end;
494         MonoObject *exc;
495
496         mono_native_thread_set_name (mono_native_thread_id_get (), "Attach receiver");
497
498         printf ("attach: Listening on '%s'...\n", server_uri);
499
500         while (TRUE) {
501                 conn_fd = accept (listen_fd, NULL, NULL);
502                 if (conn_fd == -1)
503                         /* Probably closed by mono_attach_cleanup () */
504                         return 0;
505
506                 printf ("attach: Connected.\n");
507
508                 MonoThread *thread = mono_thread_attach (mono_get_root_domain ());
509                 mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Attach receiver"), TRUE, FALSE, &error);
510                 mono_error_assert_ok (&error);
511                 /* Ask the runtime to not abort this thread */
512                 //mono_thread_current ()->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
513                 /* Ask the runtime to not wait for this thread */
514                 thread->internal_thread->state |= ThreadState_Background;
515
516                 while (TRUE) {
517                         char *cmd, *agent_name, *agent_args;
518                         guint8 *body;
519
520                         /* Read Header */
521                         res = read (conn_fd, buffer, 6);
522
523                         if (res == -1 && errno == EINTR)
524                                 continue;
525
526                         if (res == -1 || stop_receiver_thread)
527                                 break;
528
529                         if (res != 6)
530                                 break;
531
532                         if ((strncmp ((char*)buffer, "MONO", 4) != 0) || buffer [4] != 1 || buffer [5] != 0) {
533                                 fprintf (stderr, "attach: message from server has unknown header.\n");
534                                 break;
535                         }
536
537                         /* Read content length */
538                         res = read (conn_fd, buffer, 4);
539                         if (res != 4)
540                                 break;
541
542                         p = buffer;
543                         p_end = p + 8;
544
545                         content_len = decode_int (p, &p, p_end);
546
547                         /* Read message body */
548                         body = (guint8 *)g_malloc (content_len);
549                         res = read (conn_fd, body, content_len);
550                         
551                         p = body;
552                         p_end = body + content_len;
553
554                         cmd = decode_string_value (p, &p, p_end);
555                         if (cmd == NULL)
556                                 break;
557                         g_assert (!strcmp (cmd, "attach"));
558
559                         agent_name = decode_string_value (p, &p, p_end);
560                         agent_args = decode_string_value (p, &p, p_end);
561
562                         printf ("attach: Loading agent '%s'.\n", agent_name);
563                         mono_attach_load_agent (mono_domain_get (), agent_name, agent_args, &exc);
564
565                         g_free (body);
566
567                         // FIXME: Send back a result
568                 }
569
570                 close (conn_fd);
571                 conn_fd = 0;
572
573                 printf ("attach: Disconnected.\n");
574
575                 if (stop_receiver_thread)
576                         break;
577         }
578
579         return 0;
580 }
581
582 #else /* DISABLE_ATTACH */
583
584 void
585 mono_attach_parse_options (char *options)
586 {
587 }
588
589 void
590 mono_attach_init (void)
591 {
592 }
593
594 gboolean
595 mono_attach_start (void)
596 {
597         return FALSE;
598 }
599
600 void
601 mono_attach_maybe_start (void)
602 {
603 }
604
605 void
606 mono_attach_cleanup (void)
607 {
608 }
609
610 #endif /* DISABLE_ATTACH */