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
36void
37fatal(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
54void
55corrupt(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
63again:
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
153int
154main(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