xref: /openbsd/games/hangman/ksyms.c (revision 274d7c50)
1 /*	$OpenBSD: ksyms.c,v 1.12 2019/06/28 13:32:52 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 <ctype.h>
20 #include <curses.h>
21 #include <elf.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 #include "hangman.h"
30 
31 static int ksyms_elf_parse(void);
32 
33 void
34 sym_getword(void)
35 {
36 	uint tries;
37 	off_t pos;
38 	int buflen;
39 	char symbuf[1 + BUFSIZ], *sym, *end;
40 	size_t symlen;
41 
42 	for (tries = 0; tries < MAXBADWORDS; tries++) {
43 		pos = arc4random_uniform(symsize);
44 		if (lseek(symfd, pos + symoffs, SEEK_SET) == -1)
45 			continue;
46 		buflen = read(symfd, symbuf, BUFSIZ);
47 		if (buflen == -1)
48 			continue;
49 
50 		/*
51 		 * The buffer is hopefully large enough to hold at least
52 		 * a complete symbol, i.e. two occurences of NUL, or
53 		 * one occurence of NUL and the buffer containing the end
54 		 * of the string table. We make sure the buffer will be
55 		 * NUL terminated in all cases.
56 		 */
57 		if (buflen + pos >= symsize)
58 			buflen = symsize - pos;
59 		*(end = symbuf + buflen) = '\0';
60 
61 		for (sym = symbuf; *sym != '\0'; sym++)
62 			;
63 		if (sym == end)
64 			continue;
65 
66 		symlen = strlen(++sym);
67 		if (symlen < MINLEN || symlen > MAXLEN)
68 			continue;
69 
70 		/* ignore symbols containing dots or dollar signs */
71 		if (strchr(sym, '.') != NULL || strchr(sym, '$') != NULL)
72 			continue;
73 
74 		break;
75 	}
76 
77 	if (tries >= MAXBADWORDS) {
78 		mvcur(0, COLS - 1, LINES -1, 0);
79 		endwin();
80 		errx(1, "can't seem a suitable symbol in %s",
81 		    Dict_name);
82 	}
83 
84 	strlcpy(Word, sym, sizeof Word);
85 	strlcpy(Known, sym, sizeof Known);
86 	for (sym = Known; *sym != '\0'; sym++) {
87 		if (*sym == '-')
88 			*sym = '_';	/* try not to confuse player */
89 		if (isalnum((unsigned char)*sym))
90 			*sym = '-';
91 	}
92 }
93 
94 int
95 sym_setup(void)
96 {
97 	if ((symfd = open(Dict_name, O_RDONLY)) == -1)
98 		return -1;
99 
100 	if (ksyms_elf_parse() == 0)
101 		return 0;
102 
103 	close(symfd);
104 	errno = ENOEXEC;
105 	return -1;
106 }
107 
108 int
109 ksyms_elf_parse(void)
110 {
111 	Elf_Ehdr eh;
112 	Elf_Shdr sh;
113 	uint s;
114 
115 	if (lseek(symfd, 0, SEEK_SET) == -1)
116 		return -1;
117 
118 	if (read(symfd, &eh, sizeof eh) != sizeof eh)
119 		return -1;
120 
121 	if (!IS_ELF(eh))
122 		return -1;
123 
124 	if (lseek(symfd, eh.e_shoff, SEEK_SET) == -1)
125 		return -1;
126 
127 	symoffs = 0;
128 	symsize = 0;
129 
130 	for (s = 0; s < eh.e_shnum; s++) {
131 		if (read(symfd, &sh, sizeof sh) != sizeof sh)
132 			return -1;
133 
134 		/*
135 		 * There should be two string table sections, one with the
136 		 * name of the sections themselves, and one with the symbol
137 		 * names. Just pick the largest one.
138 		 */
139 		if (sh.sh_type == SHT_STRTAB) {
140 			if (symsize > (off_t)sh.sh_size)
141 				continue;
142 
143 			symoffs = (off_t)sh.sh_offset;
144 			symsize = (off_t)sh.sh_size;
145 		}
146 	}
147 
148 	if (symsize == 0)
149 		return -1;
150 
151 	return 0;
152 }
153