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