1 /*
2  * Copyright 2012 Red Hat, Inc.
3  * All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; version 2 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Author(s): Peter Jones <pjones@redhat.com>
18  */
19 
20 #include "libdpe.h"
21 
22 #include <err.h>
23 #include <unistd.h>
24 #include <sys/mman.h>
25 #include <sys/types.h>
26 
27 #define adjust(x,y) ((x) = (typeof (x))(((uint8_t *)(x)) + (y)))
28 static void
pe_fix_addresses(Pe * pe,int64_t offset)29 pe_fix_addresses(Pe *pe, int64_t offset)
30 {
31 	pe->map_address += offset;
32 
33 	adjust(pe->state.pe.mzhdr, offset);
34 	adjust(pe->state.pe.pehdr, offset);
35 	adjust(pe->state.pe.reserved0, offset);
36 	adjust(pe->state.pe.reserved1, offset);
37 	adjust(pe->state.pe.shdr, offset);
38 
39 	size_t scncnt = get_shnum(pe->map_address, pe->maximum_size);
40 	/* in general this means we couldn't identify any sections, so
41 	 * they must not need to be fixed up. */
42 	if (scncnt == (size_t)-1l)
43 		return;
44 
45 	for (size_t cnt = 0; cnt < scncnt; cnt++) {
46 		pe->state.pe.scns.data[cnt].shdr =
47 			&pe->state.pe.shdr[cnt];
48 
49 		adjust(pe->state.pe.scns.data[cnt].rawdata_base, offset);
50 		adjust(pe->state.pe.scns.data[cnt].data_base, offset);
51 	}
52 }
53 #undef adjust
54 
55 #define align(val, align) (((val) + (align) -1 ) & (- (align)))
56 
57 int
pe_set_image_size(Pe * pe)58 pe_set_image_size(Pe *pe)
59 {
60 	uint32_t image_size = 0;
61 	struct pe_hdr *pehdr = pe->state.pe.pehdr;
62 	struct pe32plus_opt_hdr *opthdr = pe->state.pe32plus_exe.opthdr;
63 
64 	Pe_Scn *scn = NULL;
65 	struct section_header shdr, tmp_shdr;
66 	for (int i = 0; i < pehdr->sections; i++) {
67 		scn = pe_nextscn(pe, scn);
68 		if (scn == NULL)
69 			break;
70 		pe_getshdr(scn, &tmp_shdr);
71 		if (tmp_shdr.virtual_size > 0)
72 			memcpy (&shdr, &tmp_shdr, sizeof(shdr));
73 	}
74 
75 	int falign = pe_get_file_alignment(pe);
76 	int salign = pe_get_scn_alignment(pe);
77 	image_size = shdr.virtual_address - opthdr->image_base +
78 		align(align(shdr.virtual_size, falign), salign);
79 
80 	pe->state.pe32plus_exe.opthdr->image_size = image_size;
81 	return 0;
82 }
83 
84 int
pe_extend_file(Pe * pe,size_t size,uint32_t * new_space,int align)85 pe_extend_file(Pe *pe, size_t size, uint32_t *new_space, int align)
86 {
87 	int error;
88 	void *new = NULL;
89 
90 	if (align)
91 		align = ALIGNMENT_PADDING(pe->maximum_size, align);
92 	int extra = size + align;
93 
94 	int rc = ftruncate(pe->fildes, pe->maximum_size + extra);
95 	if (rc < 0)
96 		return -1;
97 
98 	error = munmap(pe->map_address, pe->maximum_size);
99 	if (error != 0)
100 		err(1, "munmap");
101 	new = mmap(pe->map_address, pe->maximum_size + extra,
102 	    PROT_WRITE | PROT_READ, MAP_SHARED, pe->fildes, 0);
103 	if (new == MAP_FAILED) {
104 		err(1, "mmap");
105 		__libpe_seterrno (PE_E_NOMEM);
106 		return -1;
107 	}
108 	if (new != pe->map_address)
109 		pe_fix_addresses(pe, (uint8_t *)new-(uint8_t *)pe->map_address);
110 
111 	char *addr = compute_mem_addr(pe, pe->maximum_size);
112 	memset(addr, '\0', extra);
113 
114 	*new_space = compute_file_addr(pe, addr + align);
115 
116 	pe->maximum_size = pe->maximum_size + extra;
117 
118 	return 0;
119 }
120 
121 int
pe_shorten_file(Pe * pe,size_t size)122 pe_shorten_file(Pe *pe, size_t size)
123 {
124 	void *new = NULL;
125 	int error;
126 
127 	error = munmap(pe->map_address, pe->maximum_size);
128 	if (error != 0)
129 		err(1, "munmap");
130 
131 	new = mmap(pe->map_address, pe->maximum_size - size, PROT_READ | PROT_WRITE, MAP_SHARED, pe->fildes, 0);
132 	if (new == MAP_FAILED) {
133 		err(1, "mmap");
134 		__libpe_seterrno (PE_E_NOMEM);
135 		return -1;
136 	}
137 
138 	int rc = ftruncate(pe->fildes, pe->maximum_size - size);
139 	if (rc < 0)
140 		return -1;
141 
142 	pe->maximum_size -= size;
143 	return 0;
144 }
145 
146 int
pe_freespace(Pe * pe,uint32_t offset,size_t size)147 pe_freespace(Pe *pe, uint32_t offset, size_t size)
148 {
149 	void *addr = compute_mem_addr(pe, offset);
150 	memset(addr, '\0', size);
151 
152 	if (offset + size == pe->maximum_size)
153 		pe_shorten_file(pe, size);
154 
155 	/* XXX PJFIX TODO: this should actually de-allocate the space, *if*
156 	 * it's the certificate list, when it isn't at the end of the file,
157 	 * too. */
158 
159 	return 0;
160 }
161