1 /* Return string pointer from string section.
2 Copyright (C) 1998-2002, 2004, 2008, 2009, 2015 Red Hat, Inc.
3 This file is part of elfutils.
4 Contributed by Ulrich Drepper <drepper@redhat.com>, 1998.
5
6 This file is free software; you can redistribute it and/or modify
7 it under the terms of either
8
9 * the GNU Lesser General Public License as published by the Free
10 Software Foundation; either version 3 of the License, or (at
11 your option) any later version
12
13 or
14
15 * the GNU General Public License as published by the Free
16 Software Foundation; either version 2 of the License, or (at
17 your option) any later version
18
19 or both in parallel, as here.
20
21 elfutils is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
25
26 You should have received copies of the GNU General Public License and
27 the GNU Lesser General Public License along with this program. If
28 not, see <http://www.gnu.org/licenses/>. */
29
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #include <libelf.h>
35 #include <stdbool.h>
36 #include <stddef.h>
37
38 #include "libelfP.h"
39
40
41 static void *
get_zdata(Elf_Scn * strscn)42 get_zdata (Elf_Scn *strscn)
43 {
44 size_t zsize, zalign;
45 void *zdata = __libelf_decompress_elf (strscn, &zsize, &zalign);
46 if (zdata == NULL)
47 return NULL;
48
49 strscn->zdata_base = zdata;
50 strscn->zdata_size = zsize;
51 strscn->zdata_align = zalign;
52
53 return zdata;
54 }
55
validate_str(const char * str,size_t from,size_t to)56 static bool validate_str (const char *str, size_t from, size_t to)
57 {
58 #if HAVE_DECL_MEMRCHR
59 return memrchr (&str[from], '\0', to - from) != NULL;
60 #else
61 do {
62 if (to <= from)
63 return false;
64
65 to--;
66 } while (str[to]);
67
68 return true;
69 #endif
70 }
71
72 char *
elf_strptr(Elf * elf,size_t idx,size_t offset)73 elf_strptr (Elf *elf, size_t idx, size_t offset)
74 {
75 if (elf == NULL)
76 return NULL;
77
78 if (elf->kind != ELF_K_ELF)
79 {
80 __libelf_seterrno (ELF_E_INVALID_HANDLE);
81 return NULL;
82 }
83
84 rwlock_rdlock (elf->lock);
85
86 char *result = NULL;
87 Elf_Scn *strscn;
88
89 /* Find the section in the list. */
90 Elf_ScnList *runp = (elf->class == ELFCLASS32
91 || (offsetof (struct Elf, state.elf32.scns)
92 == offsetof (struct Elf, state.elf64.scns))
93 ? &elf->state.elf32.scns : &elf->state.elf64.scns);
94 while (1)
95 {
96 if (idx < runp->max)
97 {
98 if (idx < runp->cnt)
99 strscn = &runp->data[idx];
100 else
101 {
102 __libelf_seterrno (ELF_E_INVALID_INDEX);
103 goto out;
104 }
105 break;
106 }
107
108 idx -= runp->max;
109
110 runp = runp->next;
111 if (runp == NULL)
112 {
113 __libelf_seterrno (ELF_E_INVALID_INDEX);
114 goto out;
115 }
116 }
117
118 size_t sh_size = 0;
119 if (elf->class == ELFCLASS32)
120 {
121 Elf32_Shdr *shdr = strscn->shdr.e32 ?: __elf32_getshdr_rdlock (strscn);
122 if (unlikely (shdr->sh_type != SHT_STRTAB))
123 {
124 /* This is no string section. */
125 __libelf_seterrno (ELF_E_INVALID_SECTION);
126 goto out;
127 }
128
129 if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
130 sh_size = shdr->sh_size;
131 else
132 {
133 if (strscn->zdata_base == NULL && get_zdata (strscn) == NULL)
134 goto out;
135 sh_size = strscn->zdata_size;
136 }
137
138 if (unlikely (offset >= sh_size))
139 {
140 /* The given offset is too big, it is beyond this section. */
141 __libelf_seterrno (ELF_E_OFFSET_RANGE);
142 goto out;
143 }
144 }
145 else
146 {
147 Elf64_Shdr *shdr = strscn->shdr.e64 ?: __elf64_getshdr_rdlock (strscn);
148 if (unlikely (shdr->sh_type != SHT_STRTAB))
149 {
150 /* This is no string section. */
151 __libelf_seterrno (ELF_E_INVALID_SECTION);
152 goto out;
153 }
154
155 if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
156 sh_size = shdr->sh_size;
157 else
158 {
159 if (strscn->zdata_base == NULL && get_zdata (strscn) == NULL)
160 goto out;
161 sh_size = strscn->zdata_size;
162 }
163
164 if (unlikely (offset >= sh_size))
165 {
166 /* The given offset is too big, it is beyond this section. */
167 __libelf_seterrno (ELF_E_OFFSET_RANGE);
168 goto out;
169 }
170 }
171
172 if (strscn->rawdata_base == NULL && ! strscn->data_read)
173 {
174 rwlock_unlock (elf->lock);
175 rwlock_wrlock (elf->lock);
176 if (strscn->rawdata_base == NULL && ! strscn->data_read
177 /* Read the section data. */
178 && __libelf_set_rawdata_wrlock (strscn) != 0)
179 goto out;
180 }
181
182 if (unlikely (strscn->zdata_base != NULL))
183 {
184 /* Make sure the string is NUL terminated. Start from the end,
185 which very likely is a NUL char. */
186 if (likely (validate_str (strscn->zdata_base, offset, sh_size)))
187 result = &strscn->zdata_base[offset];
188 else
189 __libelf_seterrno (ELF_E_INVALID_INDEX);
190 }
191 else if (likely (strscn->data_list_rear == NULL))
192 {
193 // XXX The above is currently correct since elf_newdata will
194 // make sure to convert the rawdata into the datalist if
195 // necessary. But it would be more efficient to keep the rawdata
196 // unconverted and only then iterate over the rest of the (newly
197 // added data) list. Note that when the ELF file is mmapped
198 // rawdata_base can be set while rawdata.d hasn't been
199 // initialized yet (when data_read is zero). So we cannot just
200 // look at the rawdata.d.d_size.
201
202 /* Make sure the string is NUL terminated. Start from the end,
203 which very likely is a NUL char. */
204 if (likely (validate_str (strscn->rawdata_base, offset, sh_size)))
205 result = &strscn->rawdata_base[offset];
206 else
207 __libelf_seterrno (ELF_E_INVALID_INDEX);
208 }
209 else
210 {
211 /* This is a file which is currently created. Use the list of
212 data blocks. */
213 struct Elf_Data_List *dl = &strscn->data_list;
214 while (dl != NULL)
215 {
216 if (offset >= (size_t) dl->data.d.d_off
217 && offset < dl->data.d.d_off + dl->data.d.d_size)
218 {
219 /* Make sure the string is NUL terminated. Start from
220 the end, which very likely is a NUL char. */
221 if (likely (validate_str ((char *) dl->data.d.d_buf,
222 offset - dl->data.d.d_off,
223 dl->data.d.d_size)))
224 result = ((char *) dl->data.d.d_buf
225 + (offset - dl->data.d.d_off));
226 else
227 __libelf_seterrno (ELF_E_INVALID_INDEX);
228 break;
229 }
230
231 dl = dl->next;
232 }
233 }
234
235 out:
236 rwlock_unlock (elf->lock);
237
238 return result;
239 }
240 INTDEF(elf_strptr)
241