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 <[email protected]>, 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 // Check end first, which is likely a zero terminator, to prevent function call
60 return ((to > 0 && str[to - 1] == '\0')
61 || (to - from > 0 && memrchr (&str[from], '\0', to - from - 1) != NULL));
62 #else
63 do {
64 if (to <= from)
65 return false;
66
67 to--;
68 } while (str[to]);
69
70 return true;
71 #endif
72 }
73
74 char *
elf_strptr(Elf * elf,size_t idx,size_t offset)75 elf_strptr (Elf *elf, size_t idx, size_t offset)
76 {
77 if (elf == NULL)
78 return NULL;
79
80 if (elf->kind != ELF_K_ELF)
81 {
82 __libelf_seterrno (ELF_E_INVALID_HANDLE);
83 return NULL;
84 }
85
86 rwlock_rdlock (elf->lock);
87
88 char *result = NULL;
89 Elf_Scn *strscn;
90
91 /* Find the section in the list. */
92 Elf_ScnList *runp = (elf->class == ELFCLASS32
93 || (offsetof (struct Elf, state.elf32.scns)
94 == offsetof (struct Elf, state.elf64.scns))
95 ? &elf->state.elf32.scns : &elf->state.elf64.scns);
96 while (1)
97 {
98 if (idx < runp->max)
99 {
100 if (idx < runp->cnt)
101 strscn = &runp->data[idx];
102 else
103 {
104 __libelf_seterrno (ELF_E_INVALID_INDEX);
105 goto out;
106 }
107 break;
108 }
109
110 idx -= runp->max;
111
112 runp = runp->next;
113 if (runp == NULL)
114 {
115 __libelf_seterrno (ELF_E_INVALID_INDEX);
116 goto out;
117 }
118 }
119
120 size_t sh_size = 0;
121 if (elf->class == ELFCLASS32)
122 {
123 Elf32_Shdr *shdr = strscn->shdr.e32 ?: __elf32_getshdr_rdlock (strscn);
124 if (unlikely (shdr == NULL || shdr->sh_type != SHT_STRTAB))
125 {
126 /* This is no string section. */
127 __libelf_seterrno (ELF_E_INVALID_SECTION);
128 goto out;
129 }
130
131 if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
132 sh_size = shdr->sh_size;
133 else
134 {
135 if (strscn->zdata_base == NULL && get_zdata (strscn) == NULL)
136 goto out;
137 sh_size = strscn->zdata_size;
138 }
139
140 if (unlikely (offset >= sh_size))
141 {
142 /* The given offset is too big, it is beyond this section. */
143 __libelf_seterrno (ELF_E_OFFSET_RANGE);
144 goto out;
145 }
146 }
147 else
148 {
149 Elf64_Shdr *shdr = strscn->shdr.e64 ?: __elf64_getshdr_rdlock (strscn);
150 if (unlikely (shdr == NULL || shdr->sh_type != SHT_STRTAB))
151 {
152 /* This is no string section. */
153 __libelf_seterrno (ELF_E_INVALID_SECTION);
154 goto out;
155 }
156
157 if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
158 sh_size = shdr->sh_size;
159 else
160 {
161 if (strscn->zdata_base == NULL && get_zdata (strscn) == NULL)
162 goto out;
163 sh_size = strscn->zdata_size;
164 }
165
166 if (unlikely (offset >= sh_size))
167 {
168 /* The given offset is too big, it is beyond this section. */
169 __libelf_seterrno (ELF_E_OFFSET_RANGE);
170 goto out;
171 }
172 }
173
174 if (strscn->rawdata_base == NULL && ! strscn->data_read)
175 {
176 rwlock_unlock (elf->lock);
177 rwlock_wrlock (elf->lock);
178 if (strscn->rawdata_base == NULL && ! strscn->data_read
179 /* Read the section data. */
180 && __libelf_set_rawdata_wrlock (strscn) != 0)
181 goto out;
182 }
183
184 if (unlikely (strscn->zdata_base != NULL))
185 {
186 /* Make sure the string is NUL terminated. Start from the end,
187 which very likely is a NUL char. */
188 if (likely (validate_str (strscn->zdata_base, offset, sh_size)))
189 result = &strscn->zdata_base[offset];
190 else
191 __libelf_seterrno (ELF_E_INVALID_INDEX);
192 }
193 else if (likely (strscn->data_list_rear == NULL))
194 {
195 // XXX The above is currently correct since elf_newdata will
196 // make sure to convert the rawdata into the datalist if
197 // necessary. But it would be more efficient to keep the rawdata
198 // unconverted and only then iterate over the rest of the (newly
199 // added data) list. Note that when the ELF file is mmapped
200 // rawdata_base can be set while rawdata.d hasn't been
201 // initialized yet (when data_read is zero). So we cannot just
202 // look at the rawdata.d.d_size.
203
204 /* Make sure the string is NUL terminated. Start from the end,
205 which very likely is a NUL char. */
206 if (likely (validate_str (strscn->rawdata_base, offset, sh_size)))
207 result = &strscn->rawdata_base[offset];
208 else
209 __libelf_seterrno (ELF_E_INVALID_INDEX);
210 }
211 else
212 {
213 /* This is a file which is currently created. Use the list of
214 data blocks. */
215 struct Elf_Data_List *dl = &strscn->data_list;
216 while (dl != NULL)
217 {
218 if (offset >= (size_t) dl->data.d.d_off
219 && offset < dl->data.d.d_off + dl->data.d.d_size)
220 {
221 /* Make sure the string is NUL terminated. Start from
222 the end, which very likely is a NUL char. */
223 if (likely (validate_str ((char *) dl->data.d.d_buf,
224 offset - dl->data.d.d_off,
225 dl->data.d.d_size)))
226 result = ((char *) dl->data.d.d_buf
227 + (offset - dl->data.d.d_off));
228 else
229 __libelf_seterrno (ELF_E_INVALID_INDEX);
230 break;
231 }
232
233 dl = dl->next;
234 }
235 }
236
237 out:
238 rwlock_unlock (elf->lock);
239
240 return result;
241 }
242 INTDEF(elf_strptr)
243