1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/mman.h>
22 #include <sys/types.h>
23 
24 #include <functional>
25 #include <string>
26 #include <vector>
27 
28 #include <android-base/file.h>
29 
30 namespace android {
31 namespace procinfo {
32 
33 struct MapInfo {
34   uint64_t start;
35   uint64_t end;
36   uint16_t flags;
37   uint64_t pgoff;
38   ino_t inode;
39   std::string name;
40   bool shared;
41 
MapInfoMapInfo42   MapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
43           const char* name, bool shared)
44       : start(start),
45         end(end),
46         flags(flags),
47         pgoff(pgoff),
48         inode(inode),
49         name(name),
50         shared(shared) {}
51 
MapInfoMapInfo52   MapInfo(const MapInfo& params)
53       : start(params.start),
54         end(params.end),
55         flags(params.flags),
56         pgoff(params.pgoff),
57         inode(params.inode),
58         name(params.name),
59         shared(params.shared) {}
60 };
61 
62 typedef std::function<void(const MapInfo&)> MapInfoCallback;
63 typedef std::function<void(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
64                       ino_t inode, const char* name, bool shared)> MapInfoParamsCallback;
65 
PassSpace(char ** p)66 static inline bool PassSpace(char** p) {
67   if (**p != ' ') {
68     return false;
69   }
70   while (**p == ' ') {
71     (*p)++;
72   }
73   return true;
74 }
75 
PassXdigit(char ** p)76 static inline bool PassXdigit(char** p) {
77   if (!isxdigit(**p)) {
78     return false;
79   }
80   do {
81     (*p)++;
82   } while (isxdigit(**p));
83   return true;
84 }
85 
86 // Parses a line given p pointing at proc/<pid>/maps content buffer and returns true on success
87 // and false on failure parsing. The next end of line will be replaced by null character and the
88 // immediate offset after the parsed line will be returned in next_line.
89 //
90 // Example of how a parsed line look line:
91 // 00400000-00409000 r-xp 00000000 fc:00 426998  /usr/lib/gvfs/gvfsd-http
ParseMapsFileLine(char * p,uint64_t & start_addr,uint64_t & end_addr,uint16_t & flags,uint64_t & pgoff,ino_t & inode,char ** name,bool & shared,char ** next_line)92 static inline bool ParseMapsFileLine(char* p, uint64_t& start_addr, uint64_t& end_addr, uint16_t& flags,
93                       uint64_t& pgoff, ino_t& inode, char** name, bool& shared, char** next_line) {
94   // Make end of line be null
95   *next_line = strchr(p, '\n');
96   if (*next_line != nullptr) {
97     **next_line = '\0';
98     (*next_line)++;
99   }
100 
101   char* end;
102   // start_addr
103   start_addr = strtoull(p, &end, 16);
104   if (end == p || *end != '-') {
105     return false;
106   }
107   p = end + 1;
108   // end_addr
109   end_addr = strtoull(p, &end, 16);
110   if (end == p) {
111     return false;
112   }
113   p = end;
114   if (!PassSpace(&p)) {
115     return false;
116   }
117   // flags
118   flags = 0;
119   if (*p == 'r') {
120     flags |= PROT_READ;
121   } else if (*p != '-') {
122     return false;
123   }
124   p++;
125   if (*p == 'w') {
126     flags |= PROT_WRITE;
127   } else if (*p != '-') {
128     return false;
129   }
130   p++;
131   if (*p == 'x') {
132     flags |= PROT_EXEC;
133   } else if (*p != '-') {
134     return false;
135   }
136   p++;
137   if (*p != 'p' && *p != 's') {
138     return false;
139   }
140   shared = *p == 's';
141 
142   p++;
143   if (!PassSpace(&p)) {
144     return false;
145   }
146   // pgoff
147   pgoff = strtoull(p, &end, 16);
148   if (end == p) {
149     return false;
150   }
151   p = end;
152   if (!PassSpace(&p)) {
153     return false;
154   }
155   // major:minor
156   if (!PassXdigit(&p) || *p++ != ':' || !PassXdigit(&p) || !PassSpace(&p)) {
157     return false;
158   }
159   // inode
160   inode = strtoull(p, &end, 10);
161   if (end == p) {
162     return false;
163   }
164   p = end;
165 
166   if (*p != '\0' && !PassSpace(&p)) {
167     return false;
168   }
169 
170   *name = p;
171 
172   return true;
173 }
174 
ReadMapFileContent(char * content,const MapInfoParamsCallback & callback)175 inline bool ReadMapFileContent(char* content, const MapInfoParamsCallback& callback) {
176   uint64_t start_addr;
177   uint64_t end_addr;
178   uint16_t flags;
179   uint64_t pgoff;
180   ino_t inode;
181   char* line_start = content;
182   char* next_line;
183   char* name;
184   bool shared;
185 
186   while (line_start != nullptr && *line_start != '\0') {
187     bool parsed = ParseMapsFileLine(line_start, start_addr, end_addr, flags, pgoff,
188                                     inode, &name, shared, &next_line);
189     if (!parsed) {
190       return false;
191     }
192 
193     line_start = next_line;
194     callback(start_addr, end_addr, flags, pgoff, inode, name, shared);
195   }
196   return true;
197 }
198 
ReadMapFileContent(char * content,const MapInfoCallback & callback)199 inline bool ReadMapFileContent(char* content, const MapInfoCallback& callback) {
200   uint64_t start_addr;
201   uint64_t end_addr;
202   uint16_t flags;
203   uint64_t pgoff;
204   ino_t inode;
205   char* line_start = content;
206   char* next_line;
207   char* name;
208   bool shared;
209 
210   while (line_start != nullptr && *line_start != '\0') {
211     bool parsed = ParseMapsFileLine(line_start, start_addr, end_addr, flags, pgoff,
212                                     inode, &name, shared, &next_line);
213     if (!parsed) {
214       return false;
215     }
216 
217     line_start = next_line;
218     callback(MapInfo(start_addr, end_addr, flags, pgoff, inode, name, shared));
219   }
220   return true;
221 }
222 
ReadMapFile(const std::string & map_file,const MapInfoCallback & callback)223 inline bool ReadMapFile(const std::string& map_file,
224                 const MapInfoCallback& callback) {
225   std::string content;
226   if (!android::base::ReadFileToString(map_file, &content)) {
227     return false;
228   }
229   return ReadMapFileContent(&content[0], callback);
230 }
231 
ReadMapFile(const std::string & map_file,const MapInfoParamsCallback & callback)232 inline bool ReadMapFile(const std::string& map_file,
233                 const MapInfoParamsCallback& callback) {
234   std::string content;
235   if (!android::base::ReadFileToString(map_file, &content)) {
236     return false;
237   }
238   return ReadMapFileContent(&content[0], callback);
239 }
240 
ReadProcessMaps(pid_t pid,const MapInfoCallback & callback)241 inline bool ReadProcessMaps(pid_t pid, const MapInfoCallback& callback) {
242   return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
243 }
244 
ReadProcessMaps(pid_t pid,const MapInfoParamsCallback & callback)245 inline bool ReadProcessMaps(pid_t pid, const MapInfoParamsCallback& callback) {
246   return ReadMapFile("/proc/" + std::to_string(pid) + "/maps", callback);
247 }
248 
ReadProcessMaps(pid_t pid,std::vector<MapInfo> * maps)249 inline bool ReadProcessMaps(pid_t pid, std::vector<MapInfo>* maps) {
250   return ReadProcessMaps(pid, [&](const MapInfo& mapinfo) { maps->emplace_back(mapinfo); });
251 }
252 
253 // Reads maps file and executes given callback for each mapping
254 // Warning: buffer should not be modified asynchronously while this function executes
255 template <class CallbackType>
ReadMapFileAsyncSafe(const char * map_file,void * buffer,size_t buffer_size,const CallbackType & callback)256 inline bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
257                                  const CallbackType& callback) {
258   if (buffer == nullptr || buffer_size == 0) {
259     return false;
260   }
261 
262   int fd = open(map_file, O_RDONLY | O_CLOEXEC);
263   if (fd == -1) {
264     return false;
265   }
266 
267   char* char_buffer = reinterpret_cast<char*>(buffer);
268   size_t start = 0;
269   size_t read_bytes = 0;
270   char* line = nullptr;
271   bool read_complete = false;
272   while (true) {
273     ssize_t bytes =
274         TEMP_FAILURE_RETRY(read(fd, char_buffer + read_bytes, buffer_size - read_bytes - 1));
275     if (bytes <= 0) {
276       if (read_bytes == 0) {
277         close(fd);
278         return bytes == 0;
279       }
280       // Treat the last piece of data as the last line.
281       char_buffer[start + read_bytes] = '\n';
282       bytes = 1;
283       read_complete = true;
284     }
285     read_bytes += bytes;
286 
287     while (read_bytes > 0) {
288       char* newline = reinterpret_cast<char*>(memchr(&char_buffer[start], '\n', read_bytes));
289       if (newline == nullptr) {
290         break;
291       }
292       *newline = '\0';
293       line = &char_buffer[start];
294       start = newline - char_buffer + 1;
295       read_bytes -= newline - line + 1;
296 
297       // Ignore the return code, errors are okay.
298       ReadMapFileContent(line, callback);
299     }
300 
301     if (read_complete) {
302       close(fd);
303       return true;
304     }
305 
306     if (start == 0 && read_bytes == buffer_size - 1) {
307       // The buffer provided is too small to contain this line, give up
308       // and indicate failure.
309       close(fd);
310       return false;
311     }
312 
313     // Copy any leftover data to the front  of the buffer.
314     if (start > 0) {
315       if (read_bytes > 0) {
316         memmove(char_buffer, &char_buffer[start], read_bytes);
317       }
318       start = 0;
319     }
320   }
321 }
322 
323 } /* namespace procinfo */
324 } /* namespace android */
325