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