1/*
2 * Copyright (c) 2013-2019, Intel Corporation
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 *  * Redistributions of source code must retain the above copyright notice,
8 *    this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright notice,
10 *    this list of conditions and the following disclaimer in the documentation
11 *    and/or other materials provided with the distribution.
12 *  * Neither the name of Intel Corporation nor the names of its contributors
13 *    may be used to endorse or promote products derived from this software
14 *    without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * 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#include "pt_section.h"
30#include "pt_section_file.h"
31
32#include "intel-pt.h"
33
34#include <stdlib.h>
35#include <string.h>
36
37
38static int fmap_init(struct pt_sec_file_mapping *mapping)
39{
40	if (!mapping)
41		return -pte_internal;
42
43	memset(mapping, 0, sizeof(*mapping));
44
45#if defined(FEATURE_THREADS)
46	{
47		int errcode;
48
49		errcode = mtx_init(&mapping->lock, mtx_plain);
50		if (errcode != thrd_success)
51			return -pte_bad_lock;
52	}
53#endif /* defined(FEATURE_THREADS) */
54
55	return 0;
56}
57
58static void fmap_fini(struct pt_sec_file_mapping *mapping)
59{
60	if (!mapping)
61		return;
62
63	fclose(mapping->file);
64
65#if defined(FEATURE_THREADS)
66
67	mtx_destroy(&mapping->lock);
68
69#endif /* defined(FEATURE_THREADS) */
70}
71
72static int fmap_lock(struct pt_sec_file_mapping *mapping)
73{
74	if (!mapping)
75		return -pte_internal;
76
77#if defined(FEATURE_THREADS)
78	{
79		int errcode;
80
81		errcode = mtx_lock(&mapping->lock);
82		if (errcode != thrd_success)
83			return -pte_bad_lock;
84	}
85#endif /* defined(FEATURE_THREADS) */
86
87	return 0;
88}
89
90static int fmap_unlock(struct pt_sec_file_mapping *mapping)
91{
92	if (!mapping)
93		return -pte_internal;
94
95#if defined(FEATURE_THREADS)
96	{
97		int errcode;
98
99		errcode = mtx_unlock(&mapping->lock);
100		if (errcode != thrd_success)
101			return -pte_bad_lock;
102	}
103#endif /* defined(FEATURE_THREADS) */
104
105	return 0;
106}
107
108int pt_sec_file_map(struct pt_section *section, FILE *file)
109{
110	struct pt_sec_file_mapping *mapping;
111	uint64_t offset, size;
112	long begin, end, fsize;
113	int errcode;
114
115	if (!section)
116		return -pte_internal;
117
118	mapping = section->mapping;
119	if (mapping)
120		return -pte_internal;
121
122	offset = section->offset;
123	size = section->size;
124
125	begin = (long) offset;
126	end = begin + (long) size;
127
128	/* Check for overflows. */
129	if ((uint64_t) begin != offset)
130		return -pte_bad_image;
131
132	if ((uint64_t) end != (offset + size))
133		return -pte_bad_image;
134
135	if (end < begin)
136		return -pte_bad_image;
137
138	/* Validate that the section lies within the file. */
139	errcode = fseek(file, 0, SEEK_END);
140	if (errcode)
141		return -pte_bad_image;
142
143	fsize = ftell(file);
144	if (fsize < 0)
145		return -pte_bad_image;
146
147	if (fsize < end)
148		return -pte_bad_image;
149
150	mapping = malloc(sizeof(*mapping));
151	if (!mapping)
152		return -pte_nomem;
153
154	errcode = fmap_init(mapping);
155	if (errcode < 0)
156		goto out_mem;
157
158	mapping->file = file;
159	mapping->begin = begin;
160	mapping->end = end;
161
162	section->mapping = mapping;
163	section->unmap = pt_sec_file_unmap;
164	section->read = pt_sec_file_read;
165	section->memsize = pt_sec_file_memsize;
166
167	return 0;
168
169out_mem:
170	free(mapping);
171	return errcode;
172}
173
174int pt_sec_file_unmap(struct pt_section *section)
175{
176	struct pt_sec_file_mapping *mapping;
177
178	if (!section)
179		return -pte_internal;
180
181	mapping = section->mapping;
182
183	if (!mapping || !section->unmap || !section->read || !section->memsize)
184		return -pte_internal;
185
186	section->mapping = NULL;
187	section->unmap = NULL;
188	section->read = NULL;
189	section->memsize = NULL;
190
191	fmap_fini(mapping);
192	free(mapping);
193
194	return 0;
195}
196
197int pt_sec_file_read(const struct pt_section *section, uint8_t *buffer,
198		     uint16_t size, uint64_t offset)
199{
200	struct pt_sec_file_mapping *mapping;
201	FILE *file;
202	long begin;
203	size_t read;
204	int errcode;
205
206	if (!buffer || !section)
207		return -pte_internal;
208
209	mapping = section->mapping;
210	if (!mapping)
211		return -pte_internal;
212
213	file = mapping->file;
214
215	/* We already checked in pt_section_read() that the requested memory
216	 * lies within the section's boundaries.
217	 *
218	 * And we checked that the file covers the entire section in
219	 * pt_sec_file_map().  There's no need to check for overflows, again.
220	 */
221	begin = mapping->begin + (long) offset;
222
223	errcode = fmap_lock(mapping);
224	if (errcode < 0)
225		return errcode;
226
227	errcode = fseek(file, begin, SEEK_SET);
228	if (errcode)
229		goto out_unlock;
230
231	read = fread(buffer, 1, size, file);
232
233	errcode = fmap_unlock(mapping);
234	if (errcode < 0)
235		return errcode;
236
237	return (int) read;
238
239out_unlock:
240	(void) fmap_unlock(mapping);
241	return -pte_nomap;
242}
243
244int pt_sec_file_memsize(const struct pt_section *section, uint64_t *size)
245{
246	if (!section || !size)
247		return -pte_internal;
248
249	if (!section->mapping)
250		return -pte_internal;
251
252	*size = 0ull;
253
254	return 0;
255}
256