1#
2# This file and its contents are supplied under the terms of the
3# Common Development and Distribution License ("CDDL"), version 1.0.
4# You may only use this file in accordance with the terms of version
5# 1.0 of the CDDL.
6#
7# A full copy of the text of the CDDL should have accompanied this
8# source.  A copy of the CDDL is also available via the Internet at
9# http://www.illumos.org/license/CDDL.
10#
11
12#
13# Copyright (c) 2017, Joyent, Inc. All rights reserved.
14#
15
16#
17# This test attempts to reproduce a three-way deadlock between mod_lock,
18# dtrace_lock and P_PR_LOCK that is induced by shmsys having to go through
19# mod_hold_stub.
20#
21if [ $# != 1 ]; then
22	echo expected one argument: '<'dtrace-path'>'
23	exit 2
24fi
25
26dtrace=$1
27DIR=/var/tmp/dtest.$$
28
29mkdir $DIR
30cd $DIR
31
32cat > prov.d <<EOF
33provider test_prov {
34	probe ripraf();
35};
36EOF
37
38$dtrace -h -s prov.d
39if [ $? -ne 0 ]; then
40	print -u2 "failed to generate header file"
41	exit 1
42fi
43
44cat > test.c <<EOF
45#include <unistd.h>
46#include <stdlib.h>
47#include <sys/types.h>
48#include <sys/ipc.h>
49#include <sys/shm.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include "prov.h"
53
54void
55main(int argc)
56{
57	void *addr;
58	int shmid;
59
60	if (argc > 1) {
61		TEST_PROV_RIPRAF();
62		exit(0);
63	}
64
65	shmid = shmget(IPC_PRIVATE, sizeof (int), IPC_CREAT | 0666);
66
67	if (shmid == -1) {
68		perror("shmget: ");
69		exit(1);
70	}
71
72	if ((addr = shmat(shmid, NULL, 0)) == (void *)-1) {
73		perror("shmat: ");
74		exit(1);
75	}
76
77	printf("%p\n", addr);
78
79	for (;;) {
80		TEST_PROV_RIPRAF();
81		sleep(1);
82	}
83}
84EOF
85
86gcc -m32 -c test.c
87if [ $? -ne 0 ]; then
88	print -u2 "failed to compile test.c"
89	exit 1
90fi
91
92$dtrace -G -32 -s prov.d test.o
93
94if [ $? -ne 0 ]; then
95	print -u2 "failed to create DOF"
96	exit 1
97fi
98
99gcc -m32 -o test test.o prov.o
100
101if [ $? -ne 0 ]; then
102	print -u2 "failed to link final executable"
103	exit 1
104fi
105
106#
107# Kick off the victim program.
108#
109./test &
110
111victim=$!
112
113#
114# Kick off a shell that will do nothing but read our victim's /proc map
115#
116( while true ; do read foo < /proc/$victim/map ; done ) &
117stubby=$!
118
119#
120# Kick off a shell that will do nothing but instrument (and de-instrument)
121# the victim
122#
123( while true; do \
124    $dtrace -q -P test_prov$victim -n BEGIN'{exit(0)}' > /dev/null ; done ) &
125inst=$!
126
127#
128# Finally, kick off a shell that will cause lots of provider registration and
129# (importantly) de-registration
130#
131( while true; do ./test foo ; done) &
132reg=$!
133
134echo $DIR
135echo victim: $victim
136echo stubby: $stubby
137echo inst: $inst
138echo reg: $reg
139
140sleep 120
141
142kill $reg
143sleep 1
144kill $inst
145sleep 1
146kill $stubby
147sleep 1
148kill $victim
149
150#
151# If we're deadlocked, this DTrace enabling won't work (if we even make it this
152# far, which seems unlikely).  In the spirit of the deadlock, we denote our
153# success by emiting a classic Faulknerism.
154#
155raf="Maybe you're not so worthless!"
156dtrace -qn BEGIN"{printf(\"$raf\"); exit(0)}"
157
158cd /
159/usr/bin/rm -rf $DIR
160
161exit 0
162