Coverage Report

Created: 2025-11-02 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libbpf/elfutils/libelf/elf_strptr.c
Line
Count
Source
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 *
42
get_zdata (Elf_Scn *strscn)
43
2.42k
{
44
2.42k
  size_t zsize, zalign;
45
2.42k
  void *zdata = __libelf_decompress_elf (strscn, &zsize, &zalign);
46
2.42k
  if (zdata == NULL)
47
2.41k
    return NULL;
48
49
9
  strscn->zdata_base = zdata;
50
9
  strscn->zdata_size = zsize;
51
9
  strscn->zdata_align = zalign;
52
53
9
  return zdata;
54
2.42k
}
55
56
static bool validate_str (const char *str, size_t from, size_t to)
57
75.7k
{
58
75.7k
#if HAVE_DECL_MEMRCHR
59
  // Check end first, which is likely a zero terminator, to prevent function call
60
75.7k
  return ((to > 0 && str[to - 1]  == '\0')
61
37.4k
    || (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
75.7k
}
73
74
char *
75
elf_strptr (Elf *elf, size_t idx, size_t offset)
76
83.7k
{
77
83.7k
  if (elf == NULL)
78
0
    return NULL;
79
80
83.7k
  if (elf->kind != ELF_K_ELF)
81
0
    {
82
0
      __libelf_seterrno (ELF_E_INVALID_HANDLE);
83
0
      return NULL;
84
0
    }
85
86
83.7k
  rwlock_rdlock (elf->lock);
87
88
83.7k
  char *result = NULL;
89
83.7k
  Elf_Scn *strscn;
90
91
  /* Find the section in the list.  */
92
83.7k
  Elf_ScnList *runp = (elf->class == ELFCLASS32
93
83.7k
           || (offsetof (struct Elf, state.elf32.scns)
94
83.7k
         == offsetof (struct Elf, state.elf64.scns))
95
83.7k
           ? &elf->state.elf32.scns : &elf->state.elf64.scns);
96
83.7k
  while (1)
97
83.7k
    {
98
83.7k
      if (idx < runp->max)
99
83.7k
  {
100
83.7k
    if (idx < runp->cnt)
101
83.7k
      strscn = &runp->data[idx];
102
0
    else
103
0
      {
104
0
        __libelf_seterrno (ELF_E_INVALID_INDEX);
105
0
        goto out;
106
0
      }
107
83.7k
    break;
108
83.7k
  }
109
110
65
      idx -= runp->max;
111
112
65
      runp = runp->next;
113
65
      if (runp == NULL)
114
65
  {
115
65
    __libelf_seterrno (ELF_E_INVALID_INDEX);
116
65
    goto out;
117
65
  }
118
65
    }
119
120
83.7k
  size_t sh_size = 0;
121
83.7k
  if (elf->class == ELFCLASS32)
122
0
    {
123
0
      Elf32_Shdr *shdr = strscn->shdr.e32 ?: __elf32_getshdr_rdlock (strscn);
124
0
      if (unlikely (shdr == NULL || shdr->sh_type != SHT_STRTAB))
125
0
  {
126
    /* This is no string section.  */
127
0
    __libelf_seterrno (ELF_E_INVALID_SECTION);
128
0
    goto out;
129
0
  }
130
131
0
      if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
132
0
  sh_size = shdr->sh_size;
133
0
      else
134
0
  {
135
0
    if (strscn->zdata_base == NULL && get_zdata (strscn) == NULL)
136
0
      goto out;
137
0
    sh_size = strscn->zdata_size;
138
0
  }
139
140
0
      if (unlikely (offset >= sh_size))
141
0
  {
142
    /* The given offset is too big, it is beyond this section.  */
143
0
    __libelf_seterrno (ELF_E_OFFSET_RANGE);
144
0
    goto out;
145
0
  }
146
0
    }
147
83.7k
  else
148
83.7k
    {
149
83.7k
      Elf64_Shdr *shdr = strscn->shdr.e64 ?: __elf64_getshdr_rdlock (strscn);
150
83.7k
      if (unlikely (shdr == NULL || shdr->sh_type != SHT_STRTAB))
151
533
  {
152
    /* This is no string section.  */
153
533
    __libelf_seterrno (ELF_E_INVALID_SECTION);
154
533
    goto out;
155
533
  }
156
157
83.1k
      if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
158
80.5k
  sh_size = shdr->sh_size;
159
2.62k
      else
160
2.62k
  {
161
2.62k
    if (strscn->zdata_base == NULL && get_zdata (strscn) == NULL)
162
2.41k
      goto out;
163
205
    sh_size = strscn->zdata_size;
164
205
  }
165
166
80.7k
      if (unlikely (offset >= sh_size))
167
4.65k
  {
168
    /* The given offset is too big, it is beyond this section.  */
169
4.65k
    __libelf_seterrno (ELF_E_OFFSET_RANGE);
170
4.65k
    goto out;
171
4.65k
  }
172
80.7k
    }
173
174
76.1k
  if (strscn->rawdata_base == NULL && ! strscn->data_read)
175
398
    {
176
398
      rwlock_unlock (elf->lock);
177
398
      rwlock_wrlock (elf->lock);
178
398
      if (strscn->rawdata_base == NULL && ! strscn->data_read
179
  /* Read the section data.  */
180
398
    && __libelf_set_rawdata_wrlock (strscn) != 0)
181
395
  goto out;
182
398
    }
183
184
75.7k
  if (unlikely (strscn->zdata_base != NULL))
185
0
    {
186
      /* Make sure the string is NUL terminated.  Start from the end,
187
         which very likely is a NUL char.  */
188
0
      if (likely (validate_str (strscn->zdata_base, offset, sh_size)))
189
0
        result = &strscn->zdata_base[offset];
190
0
      else
191
0
        __libelf_seterrno (ELF_E_INVALID_INDEX);
192
0
    }
193
75.7k
  else if (likely (strscn->data_list_rear == NULL))
194
73.5k
    {
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
73.5k
      if (likely (validate_str (strscn->rawdata_base, offset, sh_size)))
207
73.3k
  result = &strscn->rawdata_base[offset];
208
209
      else
209
209
  __libelf_seterrno (ELF_E_INVALID_INDEX);
210
73.5k
    }
211
2.15k
  else
212
2.15k
    {
213
      /* This is a file which is currently created.  Use the list of
214
   data blocks.  */
215
2.15k
      struct Elf_Data_List *dl = &strscn->data_list;
216
2.15k
      while (dl != NULL)
217
2.15k
  {
218
2.15k
    if (offset >= (size_t) dl->data.d.d_off
219
2.15k
        && offset < dl->data.d.d_off + dl->data.d.d_size)
220
2.15k
      {
221
        /* Make sure the string is NUL terminated.  Start from
222
     the end, which very likely is a NUL char.  */
223
2.15k
        if (likely (validate_str ((char *) dl->data.d.d_buf,
224
2.15k
          offset - dl->data.d.d_off,
225
2.15k
          dl->data.d.d_size)))
226
1.93k
    result = ((char *) dl->data.d.d_buf
227
1.93k
        + (offset - dl->data.d.d_off));
228
218
        else
229
218
    __libelf_seterrno (ELF_E_INVALID_INDEX);
230
2.15k
        break;
231
2.15k
      }
232
233
0
    dl = dl->next;
234
0
  }
235
2.15k
    }
236
237
83.7k
 out:
238
83.7k
  rwlock_unlock (elf->lock);
239
240
83.7k
  return result;
241
75.7k
}
242
INTDEF(elf_strptr)