88
99#define BUILD_ID 3
1010
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+
11140/*
12141 * Parse build id from the note segment. This logic can be shared between
13142 * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are
14143 * identical.
15144 */
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 )
20148{
21149 const char note_name [] = "GNU" ;
22150 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 ;
24153 const char * data ;
25154
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 ;
29160
30161 name_sz = READ_ONCE (nhdr -> n_namesz );
31162 desc_sz = READ_ONCE (nhdr -> n_descsz );
32163
33164 new_off = note_off + sizeof (Elf32_Nhdr );
34165 if (check_add_overflow (new_off , ALIGN (name_sz , 4 ), & new_off ) ||
35166 check_add_overflow (new_off , ALIGN (desc_sz , 4 ), & new_off ) ||
36- new_off > note_size )
167+ new_off > note_end )
37168 break ;
38169
39170 if (nhdr -> n_type == BUILD_ID &&
40171 name_sz == note_name_sz &&
41172 memcmp (nhdr + 1 , note_name , note_name_sz ) == 0 &&
42173 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+
44181 memcpy (build_id , data , desc_sz );
45182 memset (build_id + desc_sz , 0 , BUILD_ID_SIZE_MAX - desc_sz );
46183 if (size )
@@ -54,30 +191,33 @@ static int parse_build_id_buf(unsigned char *build_id,
54191 return - EINVAL ;
55192}
56193
57- static inline int parse_build_id (const void * page_addr ,
194+ static inline int parse_build_id (struct freader * r ,
58195 unsigned char * build_id ,
59196 __u32 * size ,
60- const void * note_start ,
197+ loff_t note_start_off ,
61198 Elf32_Word note_size )
62199{
63200 /* 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 )
65202 return - EINVAL ;
66203
67204 /* 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 )
69206 return - EINVAL ;
70207
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 );
72209}
73210
74211/* 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 )
77213{
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 ;
81221
82222 /*
83223 * FIXME
@@ -87,30 +227,35 @@ static int get_build_id_32(const void *page_addr, unsigned char *build_id,
87227 if (ehdr -> e_phoff != sizeof (Elf32_Ehdr ))
88228 return - EINVAL ;
89229
230+ /* subsequent freader_fetch() calls invalidate pointers, so remember locally */
90231 phnum = READ_ONCE (ehdr -> e_phnum );
91232 /* only supports phdr that fits in one page */
92233 if (phnum > (PAGE_SIZE - sizeof (Elf32_Ehdr )) / sizeof (Elf32_Phdr ))
93234 return - EINVAL ;
94235
95- phdr = (Elf32_Phdr * )(page_addr + sizeof (Elf32_Ehdr ));
96-
97236 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 )))
102244 return 0 ;
103245 }
104246 return - EINVAL ;
105247}
106248
107249/* 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 )
110251{
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 ;
114259
115260 /*
116261 * FIXME
@@ -120,23 +265,29 @@ static int get_build_id_64(const void *page_addr, unsigned char *build_id,
120265 if (ehdr -> e_phoff != sizeof (Elf64_Ehdr ))
121266 return - EINVAL ;
122267
268+ /* subsequent freader_fetch() calls invalidate pointers, so remember locally */
123269 phnum = READ_ONCE (ehdr -> e_phnum );
124270 /* only supports phdr that fits in one page */
125271 if (phnum > (PAGE_SIZE - sizeof (Elf64_Ehdr )) / sizeof (Elf64_Phdr ))
126272 return - EINVAL ;
127273
128- phdr = (Elf64_Phdr * )(page_addr + sizeof (Elf64_Ehdr ));
129-
130274 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 )))
135282 return 0 ;
136283 }
284+
137285 return - EINVAL ;
138286}
139287
288+ /* enough for Elf64_Ehdr, Elf64_Phdr, and all the smaller requests */
289+ #define MAX_FREADER_BUF_SZ 64
290+
140291/*
141292 * Parse build ID of ELF file mapped to vma
142293 * @vma: vma object
@@ -148,26 +299,25 @@ static int get_build_id_64(const void *page_addr, unsigned char *build_id,
148299int build_id_parse (struct vm_area_struct * vma , unsigned char * build_id ,
149300 __u32 * size )
150301{
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 ] ;
154305 int ret ;
155306
156307 /* only works for page backed storage */
157308 if (!vma -> vm_file )
158309 return - EINVAL ;
159310
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 ;
166318 }
167319
168320 ret = - EINVAL ;
169- page_addr = kmap_local_page (page );
170- ehdr = (Elf32_Ehdr * )page_addr ;
171321
172322 /* compare magic x7f "ELF" */
173323 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,
178328 goto out ;
179329
180330 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 );
182332 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 );
184334out :
185- kunmap_local (page_addr );
186- put_page (page );
335+ freader_cleanup (& r );
187336 return ret ;
188337}
189338
@@ -197,7 +346,15 @@ int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
197346 */
198347int build_id_parse_buf (const void * buf , unsigned char * build_id , u32 buf_size )
199348{
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 ;
201358}
202359
203360#if IS_ENABLED (CONFIG_STACKTRACE_BUILD_ID ) || IS_ENABLED (CONFIG_VMCORE_INFO )
0 commit comments