1/*	$NetBSD: panic_string.c,v 1.3 2021/10/21 13:21:55 andvar Exp $	*/
2
3/*-
4 * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29
30#include <sys/cdefs.h>
31__KERNEL_RCSID(0, "$NetBSD: panic_string.c,v 1.3 2021/10/21 13:21:55 andvar Exp $");
32
33#include <sys/param.h>
34#include <sys/types.h>
35#include <sys/conf.h>
36#include <sys/device.h>
37#include <sys/filedesc.h>
38#include <sys/kernel.h>
39#include <sys/kmem.h>
40#include <sys/lwp.h>
41#include <sys/module.h>
42#include <sys/vfs_syscalls.h>
43
44/*
45 * Create a device /dev/panic from which you can read sequential
46 * user input.
47 *
48 * To use this device you need to do:
49 *      mknod /dev/panic c 351 0
50 *
51 * To write to the device you might need:
52 *      chmod 666 /dev/panic
53 *
54 * Commentary:
55 * This module manages the device /dev/panic,
56 * transfers a string from userspace to kernel space
57 * and calls kernel panic with the passed string.
58 *
59 *  echo 'string' > /dev/panic
60 * will do the trick after loading the module.
61 */
62
63dev_type_open(panic_string_open);
64dev_type_close(panic_string_close);
65dev_type_write(panic_string_write);
66
67static struct cdevsw panic_string_cdevsw = {
68	.d_open = panic_string_open,
69	.d_close = panic_string_close,
70	.d_read = noread,
71	.d_write = panic_string_write,
72	.d_ioctl = noioctl,
73	.d_stop = nostop,
74	.d_tty = notty,
75	.d_poll = nopoll,
76	.d_mmap = nommap,
77	.d_kqfilter = nokqfilter,
78	.d_discard = nodiscard,
79	.d_flag = D_OTHER
80};
81
82static struct panic_string_softc {
83	int refcnt;
84} sc;
85
86/*
87 * A function similar to strnlen + isprint
88 *
89 * Detect length of the printable and non-whitespace string in the buffer.
90 * A string is accepted if it contains any non-space character.
91 */
92
93static size_t
94printable_length(const char *str, size_t len)
95{
96	size_t n;
97	bool accepted;
98
99	n = 0;
100	accepted = false;
101
102	while (len > n) {
103		if (str[n] >= 0x20 && str[n] <= 0x7e) {
104			if (str[n] != 0x20 /* space */)
105				accepted = true;
106			n++;
107		} else
108			break;
109	}
110
111	if (accepted)
112		return n;
113	else
114		return 0;
115}
116
117int
118panic_string_open(dev_t self __unused, int flag __unused, int mod __unused, struct lwp *l)
119{
120
121	/* Make sure the device is opened once at a time */
122	if (sc.refcnt > 0)
123		return EBUSY;
124
125	++sc.refcnt;
126
127	return 0;
128}
129
130int
131panic_string_close(dev_t self __unused, int flag __unused, int mod __unused, struct lwp *l __unused)
132{
133
134	--sc.refcnt;
135	return 0;
136}
137
138int
139panic_string_write(dev_t self, struct uio *uio, int flags)
140{
141	size_t len, printlen;
142	char *buffer;
143
144	/* Buffer length */
145	len = uio->uio_iov->iov_len;
146
147	/* Allocate a local buffer to store the string */
148	buffer = (char *)kmem_alloc(len, KM_SLEEP);
149
150	/* Move the string from user to kernel space and store it locally */
151	uiomove(buffer, len, uio);
152
153	printlen = printable_length(buffer, len);
154
155	if (printlen > 0) {
156		/* Flushing disk changes */
157		do_sys_sync(curlwp);
158
159		panic("panic string: %.*s\n", (int)printlen, buffer);
160//		printf("panic string: %.*s\n", (int)printlen, buffer);
161
162		/* NOTREACHED */
163	}
164
165	kmem_free(buffer, len);
166	return 0;
167}
168
169MODULE(MODULE_CLASS_MISC, panic_string, NULL);
170
171static int
172panic_string_modcmd(modcmd_t cmd, void *arg __unused)
173{
174	/* The major should be verified and changed if needed to avoid
175	 * conflicts with other devices. */
176	int cmajor = 351, bmajor = -1;
177
178	switch (cmd) {
179	case MODULE_CMD_INIT:
180		printf("Panic String module loaded.\n");
181		if (devsw_attach("panic", NULL, &bmajor, &panic_string_cdevsw,
182						 &cmajor))
183			return ENXIO;
184		return 0;
185
186	case MODULE_CMD_FINI:
187		printf("Panic String module unloaded.\n");
188		if (sc.refcnt > 0)
189			return EBUSY;
190
191		devsw_detach(NULL, &panic_string_cdevsw);
192		return 0;
193	default:
194		return ENOTTY;
195	}
196}
197