xref: /aosp_15_r20/external/minijail/libminijailpreload.c (revision 4b9c6d91573e8b3a96609339b46361b5476dd0f9)
1 /* libminijailpreload.c - preload hack library
2  * Copyright 2011 The ChromiumOS Authors
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  *
6  * This library is preloaded into every program launched by minijail_run().
7  * DO NOT EXPORT ANY SYMBOLS FROM THIS LIBRARY. They will replace other symbols
8  * in the programs it is preloaded into and cause impossible-to-debug failures.
9  * See the minijail0.1 for a design explanation.
10  */
11 
12 #include "libminijail.h"
13 #include "libminijail-private.h"
14 #include "util.h"
15 
16 #include <dlfcn.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/types.h>
21 #include <syslog.h>
22 #include <unistd.h>
23 
24 static int (*real_main) (int, char **, char **);
25 static void *libc_handle;
26 
truncate_preload_env(char ** envp,const char * name)27 static void truncate_preload_env(char **envp, const char *name)
28 {
29 	char *env_value = minijail_getenv(envp, name);
30 	if (env_value) {
31 		/*
32 		 * if we have more than just libminijailpreload.so in
33 		 * LD_PRELOAD, cut out libminijailpreload.so from it,
34 		 * as it is guaranteed to always be last in the
35 		 * LD_PRELOAD list.
36 		 */
37 		char *last_space = strrchr(env_value, ' ');
38 		if (last_space) {
39 			*last_space = '\0';
40 		} else {
41 			/* Only our lib was in LD_PRELOAD, just unset it. */
42 			minijail_unsetenv(envp, name);
43 		}
44 	}
45 }
46 
47 /** @brief Fake main(), spliced in before the real call to main() by
48  *         __libc_start_main (see below).
49  *  We get serialized commands from our invoking process over an fd specified
50  *  by an environment variable (kFdEnvVar). The environment variable is a list
51  *  of key=value pairs (see move_commands_to_env); we use them to construct a
52  *  jail, then enter it.
53  */
fake_main(int argc,char ** argv,char ** envp)54 static int fake_main(int argc, char **argv, char **envp)
55 {
56 	char *fd_name = getenv(kFdEnvVar);
57 	int fd = -1;
58 	struct minijail *j;
59 	if (geteuid() != getuid() || getegid() != getgid()) {
60 		/*
61 		 * If we didn't do this check, an attacker could set kFdEnvVar
62 		 * for any setuid program that uses libminijail to cause it to
63 		 * get capabilities or a uid it did not expect.
64 		 */
65 		/* TODO(wad): why would libminijail interact here? */
66 		return MINIJAIL_ERR_PRELOAD;
67 	}
68 	if (!fd_name)
69 		return MINIJAIL_ERR_PRELOAD;
70 	fd = atoi(fd_name);
71 	if (fd < 0)
72 		return MINIJAIL_ERR_PRELOAD;
73 
74 	j = minijail_new();
75 	if (!j)
76 		die("preload: out of memory");
77 	if (minijail_from_fd(fd, j))
78 		die("preload: failed to parse minijail from parent");
79 	close(fd);
80 
81 	minijail_unsetenv(envp, kFdEnvVar);
82 
83 	truncate_preload_env(envp, kLdPreloadEnvVar);
84 
85 	/* Strip out flags meant for the parent. */
86 	minijail_preenter(j);
87 	minijail_enter(j);
88 	minijail_destroy(j);
89 	dlclose(libc_handle);
90 	return real_main(argc, argv, envp);
91 }
92 
93 /** @brief LD_PRELOAD override of __libc_start_main.
94  *
95  *  It is really best if you do not look too closely at this function.  We need
96  *  to ensure that some of our code runs before the target program (see the
97  *  minijail0.1 file in this directory for high-level details about this), and
98  *  the only available place to hook is this function, which is normally
99  *  responsible for calling main(). Our LD_PRELOAD will overwrite the real
100  *  __libc_start_main with this one, so we have to look up the real one from
101  *  libc and invoke it with a pointer to the fake main() we'd like to run before
102  *  the real main(). We can't just run our setup code *here* because
103  *  __libc_start_main is responsible for setting up the C runtime environment,
104  *  so we can't rely on things like malloc() being available yet.
105  */
106 
__libc_start_main(int (* main)(int,char **,char **),int argc,char ** ubp_av,void (* init)(void),void (* fini)(void),void (* rtld_fini)(void),void (* stack_end))107 int API __libc_start_main(int (*main)(int, char **, char **), int argc,
108 			  char **ubp_av, void (*init)(void), void (*fini)(void),
109 			  void (*rtld_fini)(void), void(*stack_end))
110 {
111 	void *sym;
112 	/*
113 	 * This hack is unfortunately required by C99 - casting directly from
114 	 * void* to function pointers is left undefined. See POSIX.1-2003, the
115 	 * Rationale for the specification of dlsym(), and dlsym(3). This
116 	 * deliberately violates strict-aliasing rules, but gcc can't tell.
117 	 */
118 	union {
119 		int (*fn)(int (*main)(int, char **, char **), int argc,
120 			  char **ubp_av, void (*init)(void), void (*fini)(void),
121 			  void (*rtld_fini)(void), void(*stack_end));
122 		void *symval;
123 	} real_libc_start_main;
124 
125 	/*
126 	 * We hold this handle for the duration of the real __libc_start_main()
127 	 * and drop it just before calling the real main().
128 	 */
129 	libc_handle = dlopen("libc.so.6", RTLD_NOW);
130 
131 	if (!libc_handle) {
132 		syslog(LOG_ERR, "can't dlopen() libc");
133 		/*
134 		 * We dare not use abort() here because it will run atexit()
135 		 * handlers and try to flush stdio.
136 		 */
137 		_exit(1);
138 	}
139 	sym = dlsym(libc_handle, "__libc_start_main");
140 	if (!sym) {
141 		syslog(LOG_ERR, "can't find the real __libc_start_main()");
142 		_exit(1);
143 	}
144 	real_libc_start_main.symval = sym;
145 	real_main = main;
146 
147 	/*
148 	 * Note that we swap fake_main in for main - fake_main knows that it
149 	 * should call real_main after it's done.
150 	 */
151 	return real_libc_start_main.fn(fake_main, argc, ubp_av, init, fini,
152 				       rtld_fini, stack_end);
153 }
154