xref: /openbsd/games/hangman/ksyms.c (revision 91f110e0)
1 /*	$OpenBSD: ksyms.c,v 1.4 2013/10/15 22:09:29 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Miodrag Vallat.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <unistd.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <string.h>
24 
25 #include <sys/exec.h>
26 #include <elf_abi.h>
27 
28 #include "hangman.h"
29 
30 int	ksyms_elf_parse();
31 
32 void
33 kgetword()
34 {
35 	uint tries;
36 	off_t pos;
37 	int buflen;
38 	char symbuf[1 + BUFSIZ], *sym, *end;
39 	size_t symlen;
40 
41 	for (tries = 0; tries < MAXBADWORDS; tries++) {
42 		pos = arc4random_uniform(ksymsize);
43 		if (lseek(ksyms, pos + ksymoffs, SEEK_SET) == -1)
44 			continue;
45 		buflen = read(ksyms, symbuf, BUFSIZ);
46 		if (buflen < 0)
47 			continue;
48 
49 		/*
50 		 * The buffer is hopefully large enough to hold at least
51 		 * a complete symbol, i.e. two occurences of NUL, or
52 		 * one occurence of NUL and the buffer containing the end
53 		 * of the string table. We make sure the buffer will be
54 		 * NUL terminated in all cases.
55 		 */
56 		if (buflen + pos >= ksymsize)
57 			buflen = ksymsize - pos;
58 		*(end = symbuf + buflen) = '\0';
59 
60 		for (sym = symbuf; *sym != '\0'; sym++) ;
61 		if (sym == end)
62 			continue;
63 
64 		symlen = strlen(++sym);
65 		if (symlen < MINLEN || symlen > MAXLEN)
66 			continue;
67 
68 		break;
69 	}
70 
71 	if (tries >= MAXBADWORDS) {
72 		mvcur(0, COLS - 1, LINES -1, 0);
73 		endwin();
74 		errx(1, "can't seem a suitable kernel symbol in %s",
75 		    Dict_name);
76 	}
77 
78 	strlcpy(Word, sym, sizeof Word);
79 	strlcpy(Known, sym, sizeof Known);
80 	for (sym = Known; *sym != '\0'; sym++) {
81 		if (*sym == '-')
82 			*sym = '_';	/* try not to confuse player */
83 		if (isalpha(*sym))
84 			*sym = '-';
85 	}
86 }
87 
88 int
89 ksetup()
90 {
91 	if ((ksyms = open(Dict_name, O_RDONLY)) < 0)
92 		return ksyms;
93 
94 	if (ksyms_elf_parse() == 0)
95 		return 0;
96 
97 	close(ksyms);
98 	errno = ENOEXEC;
99 	return -1;
100 }
101 
102 int
103 ksyms_elf_parse()
104 {
105 	Elf_Ehdr eh;
106 	Elf_Shdr sh;
107 	uint s;
108 
109 	if (lseek(ksyms, 0, SEEK_SET) == -1)
110 		return -1;
111 
112 	if (read(ksyms, &eh, sizeof eh) != sizeof eh)
113 		return -1;
114 
115 	if (!IS_ELF(eh))
116 		return -1;
117 
118 	if (lseek(ksyms, eh.e_shoff, SEEK_SET) == -1)
119 		return -1;
120 
121 	ksymoffs = 0;
122 	ksymsize = 0;
123 
124 	for (s = 0; s < eh.e_shnum; s++) {
125 		if (read(ksyms, &sh, sizeof sh) != sizeof sh)
126 			return -1;
127 
128 		/*
129 		 * There should be two string table sections, one with the
130 		 * name of the sections themselves, and one with the symbol
131 		 * names. Just pick the largest one.
132 		 */
133 		if (sh.sh_type == SHT_STRTAB) {
134 			if (ksymsize > (off_t)sh.sh_size)
135 				continue;
136 
137 			ksymoffs = (off_t)sh.sh_offset;
138 			ksymsize = (off_t)sh.sh_size;
139 		}
140 	}
141 
142 	if (ksymsize == 0)
143 		return -1;
144 
145 	return 0;
146 }
147