1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/stat.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <sys/varargs.h>
32 #include <errno.h>
33 #include <math.h>
34 #include <dtrace.h>
35 
36 void
fatal(char * fmt,...)37 fatal(char *fmt, ...)
38 {
39 	va_list ap;
40 
41 	va_start(ap, fmt);
42 
43 	fprintf(stderr, "%s: ", "baddof");
44 	vfprintf(stderr, fmt, ap);
45 
46 	if (fmt[strlen(fmt) - 1] != '\n')
47 		fprintf(stderr, ": %s\n", strerror(errno));
48 
49 	exit(1);
50 }
51 
52 #define	LEAP_DISTANCE		20
53 
54 void
corrupt(int fd,unsigned char * buf,int len)55 corrupt(int fd, unsigned char *buf, int len)
56 {
57 	static int ttl, valid;
58 	int bit, i;
59 	unsigned char saved;
60 	int val[LEAP_DISTANCE], pos[LEAP_DISTANCE];
61 	int new, rv;
62 
63 again:
64 	printf("valid DOF #%d\n", valid++);
65 
66 	/*
67 	 * We are going iterate through, flipping one bit and attempting
68 	 * to enable.
69 	 */
70 	for (bit = 0; bit < len * 8; bit++) {
71 		saved = buf[bit / 8];
72 		buf[bit / 8] ^= (1 << (bit % 8));
73 
74 		if ((bit % 100) == 0)
75 			printf("%d\n", bit);
76 
77 		if ((rv = ioctl(fd, DTRACEIOC_ENABLE, buf)) == -1) {
78 			/*
79 			 * That failed -- restore the bit and drive on.
80 			 */
81 			buf[bit / 8] = saved;
82 			continue;
83 		}
84 
85 		/*
86 		 * That worked -- and it may have enabled probes.  To keep
87 		 * enabled probes down to a reasonable level, we'll close
88 		 * and reopen pseudodevice if we have more than 10,000
89 		 * probes enabled.
90 		 */
91 		ttl += rv;
92 
93 		if (ttl < 10000) {
94 			buf[bit / 8] = saved;
95 			continue;
96 		}
97 
98 		printf("enabled %d probes; resetting device.\n", ttl);
99 		close(fd);
100 
101 		new = open("/devices/pseudo/dtrace@0:dtrace", O_RDWR);
102 
103 		if (new == -1)
104 			fatal("couldn't open DTrace pseudo device");
105 
106 		if (new != fd) {
107 			dup2(new, fd);
108 			close(new);
109 		}
110 
111 		ttl = 0;
112 		buf[bit / 8] = saved;
113 	}
114 
115 	for (;;) {
116 		/*
117 		 * Now we want to get as many bits away as possible.  We flip
118 		 * bits randomly -- getting as far away as we can until we don't
119 		 * seem to be making any progress.
120 		 */
121 		for (i = 0; i < LEAP_DISTANCE; i++) {
122 			/*
123 			 * Pick a random bit and corrupt it.
124 			 */
125 			bit = lrand48() % (len * 8);
126 
127 			val[i] = buf[bit / 8];
128 			pos[i] = bit / 8;
129 			buf[bit / 8] ^= (1 << (bit % 8));
130 		}
131 
132 		/*
133 		 * Let's see if that managed to get us valid DOF...
134 		 */
135 		if ((rv = ioctl(fd, DTRACEIOC_ENABLE, buf)) > 0) {
136 			/*
137 			 * Success!  This will be our new base for valid DOF.
138 			 */
139 			ttl += rv;
140 			goto again;
141 		}
142 
143 		/*
144 		 * No luck -- we'll restore those bits and try flipping a
145 		 * different set.  Note that this must be done in reverse
146 		 * order...
147 		 */
148 		for (i = LEAP_DISTANCE - 1; i >= 0; i--)
149 			buf[pos[i]] = val[i];
150 	}
151 }
152 
153 int
main(int argc,char ** argv)154 main(int argc, char **argv)
155 {
156 	char *filename = argv[1];
157 	dtrace_hdl_t *dtp;
158 	dtrace_prog_t *pgp;
159 	int err, fd, len;
160 	FILE *fp;
161 	unsigned char *dof, *copy;
162 
163 	if (argc < 2)
164 		fatal("expected D script as argument\n");
165 
166 	if ((fp = fopen(filename, "r")) == NULL)
167 		fatal("couldn't open %s", filename);
168 
169 	/*
170 	 * First, we need to compile our provided D into DOF.
171 	 */
172 	if ((dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) {
173 		fatal("cannot open dtrace library: %s\n",
174 		    dtrace_errmsg(NULL, err));
175 	}
176 
177 	pgp = dtrace_program_fcompile(dtp, fp, 0, 0, NULL);
178 	fclose(fp);
179 
180 	if (pgp == NULL) {
181 		fatal("failed to compile script %s: %s\n", filename,
182 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
183 	}
184 
185 	dof = dtrace_dof_create(dtp, pgp, 0);
186 	len = ((dof_hdr_t *)dof)->dofh_loadsz;
187 
188 	if ((copy = malloc(len)) == NULL)
189 		fatal("could not allocate copy of %d bytes", len);
190 
191 	for (;;) {
192 		bcopy(dof, copy, len);
193 		/*
194 		 * Open another instance of the dtrace device.
195 		 */
196 		fd = open("/devices/pseudo/dtrace@0:dtrace", O_RDWR);
197 
198 		if (fd == -1)
199 			fatal("couldn't open DTrace pseudo device");
200 
201 		corrupt(fd, copy, len);
202 		close(fd);
203 	}
204 
205 	/* NOTREACHED */
206 	return (0);
207 }
208