8
8
9
9
#define BUILD_ID 3
10
10
11
+ struct freader {
12
+ void * buf ;
13
+ u32 buf_sz ;
14
+ int err ;
15
+ union {
16
+ struct {
17
+ struct address_space * mapping ;
18
+ struct folio * folio ;
19
+ void * addr ;
20
+ loff_t folio_off ;
21
+ };
22
+ struct {
23
+ const char * data ;
24
+ u64 data_sz ;
25
+ };
26
+ };
27
+ };
28
+
29
+ static void freader_init_from_file (struct freader * r , void * buf , u32 buf_sz ,
30
+ struct address_space * mapping )
31
+ {
32
+ memset (r , 0 , sizeof (* r ));
33
+ r -> buf = buf ;
34
+ r -> buf_sz = buf_sz ;
35
+ r -> mapping = mapping ;
36
+ }
37
+
38
+ static void freader_init_from_mem (struct freader * r , const char * data , u64 data_sz )
39
+ {
40
+ memset (r , 0 , sizeof (* r ));
41
+ r -> data = data ;
42
+ r -> data_sz = data_sz ;
43
+ }
44
+
45
+ static void freader_put_folio (struct freader * r )
46
+ {
47
+ if (!r -> folio )
48
+ return ;
49
+ kunmap_local (r -> addr );
50
+ folio_put (r -> folio );
51
+ r -> folio = NULL ;
52
+ }
53
+
54
+ static int freader_get_folio (struct freader * r , loff_t file_off )
55
+ {
56
+ /* check if we can just reuse current folio */
57
+ if (r -> folio && file_off >= r -> folio_off &&
58
+ file_off < r -> folio_off + folio_size (r -> folio ))
59
+ return 0 ;
60
+
61
+ freader_put_folio (r );
62
+
63
+ r -> folio = filemap_get_folio (r -> mapping , file_off >> PAGE_SHIFT );
64
+ if (IS_ERR (r -> folio ) || !folio_test_uptodate (r -> folio )) {
65
+ if (!IS_ERR (r -> folio ))
66
+ folio_put (r -> folio );
67
+ r -> folio = NULL ;
68
+ return - EFAULT ;
69
+ }
70
+
71
+ r -> folio_off = folio_pos (r -> folio );
72
+ r -> addr = kmap_local_folio (r -> folio , 0 );
73
+
74
+ return 0 ;
75
+ }
76
+
77
+ static const void * freader_fetch (struct freader * r , loff_t file_off , size_t sz )
78
+ {
79
+ size_t folio_sz ;
80
+
81
+ /* provided internal temporary buffer should be sized correctly */
82
+ if (WARN_ON (r -> buf && sz > r -> buf_sz )) {
83
+ r -> err = - E2BIG ;
84
+ return NULL ;
85
+ }
86
+
87
+ if (unlikely (file_off + sz < file_off )) {
88
+ r -> err = - EOVERFLOW ;
89
+ return NULL ;
90
+ }
91
+
92
+ /* working with memory buffer is much more straightforward */
93
+ if (!r -> buf ) {
94
+ if (file_off + sz > r -> data_sz ) {
95
+ r -> err = - ERANGE ;
96
+ return NULL ;
97
+ }
98
+ return r -> data + file_off ;
99
+ }
100
+
101
+ /* fetch or reuse folio for given file offset */
102
+ r -> err = freader_get_folio (r , file_off );
103
+ if (r -> err )
104
+ return NULL ;
105
+
106
+ /* if requested data is crossing folio boundaries, we have to copy
107
+ * everything into our local buffer to keep a simple linear memory
108
+ * access interface
109
+ */
110
+ folio_sz = folio_size (r -> folio );
111
+ if (file_off + sz > r -> folio_off + folio_sz ) {
112
+ int part_sz = r -> folio_off + folio_sz - file_off ;
113
+
114
+ /* copy the part that resides in the current folio */
115
+ memcpy (r -> buf , r -> addr + (file_off - r -> folio_off ), part_sz );
116
+
117
+ /* fetch next folio */
118
+ r -> err = freader_get_folio (r , r -> folio_off + folio_sz );
119
+ if (r -> err )
120
+ return NULL ;
121
+
122
+ /* copy the rest of requested data */
123
+ memcpy (r -> buf + part_sz , r -> addr , sz - part_sz );
124
+
125
+ return r -> buf ;
126
+ }
127
+
128
+ /* if data fits in a single folio, just return direct pointer */
129
+ return r -> addr + (file_off - r -> folio_off );
130
+ }
131
+
132
+ static void freader_cleanup (struct freader * r )
133
+ {
134
+ if (!r -> buf )
135
+ return ; /* non-file-backed mode */
136
+
137
+ freader_put_folio (r );
138
+ }
139
+
11
140
/*
12
141
* Parse build id from the note segment. This logic can be shared between
13
142
* 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are
14
143
* identical.
15
144
*/
16
- static int parse_build_id_buf (unsigned char * build_id ,
17
- __u32 * size ,
18
- const void * note_start ,
19
- Elf32_Word note_size )
145
+ static int parse_build_id_buf (struct freader * r ,
146
+ unsigned char * build_id , __u32 * size ,
147
+ loff_t note_off , Elf32_Word note_size )
20
148
{
21
149
const char note_name [] = "GNU" ;
22
150
const size_t note_name_sz = sizeof (note_name );
23
- u64 note_off = 0 , new_off , name_sz , desc_sz ;
151
+ u32 build_id_off , new_off , note_end , name_sz , desc_sz ;
152
+ const Elf32_Nhdr * nhdr ;
24
153
const char * data ;
25
154
26
- while (note_off + sizeof (Elf32_Nhdr ) < note_size &&
27
- note_off + sizeof (Elf32_Nhdr ) > note_off /* overflow */ ) {
28
- Elf32_Nhdr * nhdr = (Elf32_Nhdr * )(note_start + note_off );
155
+ note_end = note_off + note_size ;
156
+ while (note_end - note_off > sizeof (Elf32_Nhdr ) + note_name_sz ) {
157
+ nhdr = freader_fetch (r , note_off , sizeof (Elf32_Nhdr ) + note_name_sz );
158
+ if (!nhdr )
159
+ return r -> err ;
29
160
30
161
name_sz = READ_ONCE (nhdr -> n_namesz );
31
162
desc_sz = READ_ONCE (nhdr -> n_descsz );
32
163
33
164
new_off = note_off + sizeof (Elf32_Nhdr );
34
165
if (check_add_overflow (new_off , ALIGN (name_sz , 4 ), & new_off ) ||
35
166
check_add_overflow (new_off , ALIGN (desc_sz , 4 ), & new_off ) ||
36
- new_off > note_size )
167
+ new_off > note_end )
37
168
break ;
38
169
39
170
if (nhdr -> n_type == BUILD_ID &&
40
171
name_sz == note_name_sz &&
41
172
memcmp (nhdr + 1 , note_name , note_name_sz ) == 0 &&
42
173
desc_sz > 0 && desc_sz <= BUILD_ID_SIZE_MAX ) {
43
- data = note_start + note_off + ALIGN (note_name_sz , 4 );
174
+ build_id_off = note_off + sizeof (Elf32_Nhdr ) + ALIGN (note_name_sz , 4 );
175
+
176
+ /* freader_fetch() will invalidate nhdr pointer */
177
+ data = freader_fetch (r , build_id_off , desc_sz );
178
+ if (!data )
179
+ return r -> err ;
180
+
44
181
memcpy (build_id , data , desc_sz );
45
182
memset (build_id + desc_sz , 0 , BUILD_ID_SIZE_MAX - desc_sz );
46
183
if (size )
@@ -54,30 +191,33 @@ static int parse_build_id_buf(unsigned char *build_id,
54
191
return - EINVAL ;
55
192
}
56
193
57
- static inline int parse_build_id (const void * page_addr ,
194
+ static inline int parse_build_id (struct freader * r ,
58
195
unsigned char * build_id ,
59
196
__u32 * size ,
60
- const void * note_start ,
197
+ loff_t note_start_off ,
61
198
Elf32_Word note_size )
62
199
{
63
200
/* check for overflow */
64
- if (note_start < page_addr || note_start + note_size < note_start )
201
+ if (note_start_off + note_size < note_start_off )
65
202
return - EINVAL ;
66
203
67
204
/* only supports note that fits in the first page */
68
- if (note_start + note_size > page_addr + PAGE_SIZE )
205
+ if (note_start_off + note_size > PAGE_SIZE )
69
206
return - EINVAL ;
70
207
71
- return parse_build_id_buf (build_id , size , note_start , note_size );
208
+ return parse_build_id_buf (r , build_id , size , note_start_off , note_size );
72
209
}
73
210
74
211
/* Parse build ID from 32-bit ELF */
75
- static int get_build_id_32 (const void * page_addr , unsigned char * build_id ,
76
- __u32 * size )
212
+ static int get_build_id_32 (struct freader * r , unsigned char * build_id , __u32 * size )
77
213
{
78
- Elf32_Ehdr * ehdr = (Elf32_Ehdr * )page_addr ;
79
- Elf32_Phdr * phdr ;
80
- __u32 i , phnum ;
214
+ const Elf32_Ehdr * ehdr ;
215
+ const Elf32_Phdr * phdr ;
216
+ __u32 phnum , i ;
217
+
218
+ ehdr = freader_fetch (r , 0 , sizeof (Elf32_Ehdr ));
219
+ if (!ehdr )
220
+ return r -> err ;
81
221
82
222
/*
83
223
* FIXME
@@ -87,30 +227,35 @@ static int get_build_id_32(const void *page_addr, unsigned char *build_id,
87
227
if (ehdr -> e_phoff != sizeof (Elf32_Ehdr ))
88
228
return - EINVAL ;
89
229
230
+ /* subsequent freader_fetch() calls invalidate pointers, so remember locally */
90
231
phnum = READ_ONCE (ehdr -> e_phnum );
91
232
/* only supports phdr that fits in one page */
92
233
if (phnum > (PAGE_SIZE - sizeof (Elf32_Ehdr )) / sizeof (Elf32_Phdr ))
93
234
return - EINVAL ;
94
235
95
- phdr = (Elf32_Phdr * )(page_addr + sizeof (Elf32_Ehdr ));
96
-
97
236
for (i = 0 ; i < phnum ; ++ i ) {
98
- if (phdr [i ].p_type == PT_NOTE &&
99
- !parse_build_id (page_addr , build_id , size ,
100
- page_addr + READ_ONCE (phdr [i ].p_offset ),
101
- READ_ONCE (phdr [i ].p_filesz )))
237
+ phdr = freader_fetch (r , i * sizeof (Elf32_Phdr ), sizeof (Elf32_Phdr ));
238
+ if (!phdr )
239
+ return r -> err ;
240
+
241
+ if (phdr -> p_type == PT_NOTE &&
242
+ !parse_build_id (r , build_id , size , READ_ONCE (phdr -> p_offset ),
243
+ READ_ONCE (phdr -> p_filesz )))
102
244
return 0 ;
103
245
}
104
246
return - EINVAL ;
105
247
}
106
248
107
249
/* Parse build ID from 64-bit ELF */
108
- static int get_build_id_64 (const void * page_addr , unsigned char * build_id ,
109
- __u32 * size )
250
+ static int get_build_id_64 (struct freader * r , unsigned char * build_id , __u32 * size )
110
251
{
111
- Elf64_Ehdr * ehdr = (Elf64_Ehdr * )page_addr ;
112
- Elf64_Phdr * phdr ;
113
- __u32 i , phnum ;
252
+ const Elf64_Ehdr * ehdr ;
253
+ const Elf64_Phdr * phdr ;
254
+ __u32 phnum , i ;
255
+
256
+ ehdr = freader_fetch (r , 0 , sizeof (Elf64_Ehdr ));
257
+ if (!ehdr )
258
+ return r -> err ;
114
259
115
260
/*
116
261
* FIXME
@@ -120,23 +265,29 @@ static int get_build_id_64(const void *page_addr, unsigned char *build_id,
120
265
if (ehdr -> e_phoff != sizeof (Elf64_Ehdr ))
121
266
return - EINVAL ;
122
267
268
+ /* subsequent freader_fetch() calls invalidate pointers, so remember locally */
123
269
phnum = READ_ONCE (ehdr -> e_phnum );
124
270
/* only supports phdr that fits in one page */
125
271
if (phnum > (PAGE_SIZE - sizeof (Elf64_Ehdr )) / sizeof (Elf64_Phdr ))
126
272
return - EINVAL ;
127
273
128
- phdr = (Elf64_Phdr * )(page_addr + sizeof (Elf64_Ehdr ));
129
-
130
274
for (i = 0 ; i < phnum ; ++ i ) {
131
- if (phdr [i ].p_type == PT_NOTE &&
132
- !parse_build_id (page_addr , build_id , size ,
133
- page_addr + READ_ONCE (phdr [i ].p_offset ),
134
- READ_ONCE (phdr [i ].p_filesz )))
275
+ phdr = freader_fetch (r , i * sizeof (Elf64_Phdr ), sizeof (Elf64_Phdr ));
276
+ if (!phdr )
277
+ return r -> err ;
278
+
279
+ if (phdr -> p_type == PT_NOTE &&
280
+ !parse_build_id (r , build_id , size , READ_ONCE (phdr -> p_offset ),
281
+ READ_ONCE (phdr -> p_filesz )))
135
282
return 0 ;
136
283
}
284
+
137
285
return - EINVAL ;
138
286
}
139
287
288
+ /* enough for Elf64_Ehdr, Elf64_Phdr, and all the smaller requests */
289
+ #define MAX_FREADER_BUF_SZ 64
290
+
140
291
/*
141
292
* Parse build ID of ELF file mapped to vma
142
293
* @vma: vma object
@@ -148,26 +299,25 @@ static int get_build_id_64(const void *page_addr, unsigned char *build_id,
148
299
int build_id_parse (struct vm_area_struct * vma , unsigned char * build_id ,
149
300
__u32 * size )
150
301
{
151
- Elf32_Ehdr * ehdr ;
152
- struct page * page ;
153
- void * page_addr ;
302
+ const Elf32_Ehdr * ehdr ;
303
+ struct freader r ;
304
+ char buf [ MAX_FREADER_BUF_SZ ] ;
154
305
int ret ;
155
306
156
307
/* only works for page backed storage */
157
308
if (!vma -> vm_file )
158
309
return - EINVAL ;
159
310
160
- page = find_get_page (vma -> vm_file -> f_mapping , 0 );
161
- if (!page )
162
- return - EFAULT ; /* page not mapped */
163
- if (!PageUptodate (page )) {
164
- put_page (page );
165
- return - EFAULT ;
311
+ freader_init_from_file (& r , buf , sizeof (buf ), vma -> vm_file -> f_mapping );
312
+
313
+ /* fetch first 18 bytes of ELF header for checks */
314
+ ehdr = freader_fetch (& r , 0 , offsetofend (Elf32_Ehdr , e_type ));
315
+ if (!ehdr ) {
316
+ ret = r .err ;
317
+ goto out ;
166
318
}
167
319
168
320
ret = - EINVAL ;
169
- page_addr = kmap_local_page (page );
170
- ehdr = (Elf32_Ehdr * )page_addr ;
171
321
172
322
/* compare magic x7f "ELF" */
173
323
if (memcmp (ehdr -> e_ident , ELFMAG , SELFMAG ) != 0 )
@@ -178,12 +328,11 @@ int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
178
328
goto out ;
179
329
180
330
if (ehdr -> e_ident [EI_CLASS ] == ELFCLASS32 )
181
- ret = get_build_id_32 (page_addr , build_id , size );
331
+ ret = get_build_id_32 (& r , build_id , size );
182
332
else if (ehdr -> e_ident [EI_CLASS ] == ELFCLASS64 )
183
- ret = get_build_id_64 (page_addr , build_id , size );
333
+ ret = get_build_id_64 (& r , build_id , size );
184
334
out :
185
- kunmap_local (page_addr );
186
- put_page (page );
335
+ freader_cleanup (& r );
187
336
return ret ;
188
337
}
189
338
@@ -197,7 +346,15 @@ int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
197
346
*/
198
347
int build_id_parse_buf (const void * buf , unsigned char * build_id , u32 buf_size )
199
348
{
200
- return parse_build_id_buf (build_id , NULL , buf , buf_size );
349
+ struct freader r ;
350
+ int err ;
351
+
352
+ freader_init_from_mem (& r , buf , buf_size );
353
+
354
+ err = parse_build_id (& r , build_id , NULL , 0 , buf_size );
355
+
356
+ freader_cleanup (& r );
357
+ return err ;
201
358
}
202
359
203
360
#if IS_ENABLED (CONFIG_STACKTRACE_BUILD_ID ) || IS_ENABLED (CONFIG_VMCORE_INFO )
0 commit comments