• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 #include "ashmem.h"
18 
19 #include <dlfcn.h>
20 #include <errno.h>
21 #include <pthread.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/mman.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/ioctl.h>
29 #include <sys/stat.h>  /* for fdstat() */
30 #include <fcntl.h>
31 
32 #include <linux/ashmem.h>
33 #include <sys/system_properties.h>
34 
35 #define ASHMEM_DEVICE  "/dev/ashmem"
36 
37 /* Technical note regarding reading system properties.
38  *
39  * Try to use the new __system_property_read_callback API that appeared in
40  * Android O / API level 26 when available. Otherwise use the deprecated
41  * __system_property_get function.
42  *
43  * For more technical details from an NDK maintainer, see:
44  * https://bugs.chromium.org/p/chromium/issues/detail?id=392191#c17
45  */
46 
47 /* Weak symbol import */
48 void __system_property_read_callback(
49     const prop_info* info,
50     void (*callback)(
51         void* cookie, const char* name, const char* value, uint32_t serial),
52     void* cookie) __attribute__((weak));
53 
54 /* Callback used with __system_property_read_callback. */
prop_read_int(void * cookie,const char * name,const char * value,uint32_t serial)55 static void prop_read_int(void* cookie,
56                           const char* name,
57                           const char* value,
58                           uint32_t serial) {
59   *(int *)cookie = atoi(value);
60   (void)name;
61   (void)serial;
62 }
63 
system_property_get_int(const char * name)64 static int system_property_get_int(const char* name) {
65   int result = 0;
66   if (__system_property_read_callback) {
67     const prop_info* info = __system_property_find(name);
68     if (info)
69       __system_property_read_callback(info, &prop_read_int, &result);
70   } else {
71     char value[PROP_VALUE_MAX] = {};
72     if (__system_property_get(name, value) >= 1)
73       result = atoi(value);
74   }
75   return result;
76 }
77 
device_api_level()78 static int device_api_level() {
79   static int s_api_level = -1;
80   if (s_api_level < 0)
81     s_api_level = system_property_get_int("ro.build.version.sdk");
82   return s_api_level;
83 }
84 
85 typedef enum {
86   ASHMEM_STATUS_INIT,
87   ASHMEM_STATUS_NOT_SUPPORTED,
88   ASHMEM_STATUS_SUPPORTED,
89 } AshmemStatus;
90 
91 static AshmemStatus s_ashmem_status = ASHMEM_STATUS_INIT;
92 static dev_t s_ashmem_dev;
93 
94 /* Return the dev_t of a given file path, or 0 if not available, */
ashmem_find_dev(const char * path)95 static dev_t ashmem_find_dev(const char* path) {
96   struct stat st;
97   dev_t result = 0;
98   if (stat(path, &st) == 0 && S_ISCHR(st.st_mode))
99     result = st.st_dev;
100   return result;
101 }
102 
ashmem_get_status(void)103 static AshmemStatus ashmem_get_status(void) {
104   /* NOTE: No need to make this thread-safe, assuming that
105    * all threads will find the same value. */
106   if (s_ashmem_status != ASHMEM_STATUS_INIT)
107     return s_ashmem_status;
108 
109   s_ashmem_dev = ashmem_find_dev(ASHMEM_DEVICE);
110   s_ashmem_status = (s_ashmem_dev == 0) ? ASHMEM_STATUS_NOT_SUPPORTED
111                                         : ASHMEM_STATUS_SUPPORTED;
112   return s_ashmem_status;
113 }
114 
115 /* Returns true iff the ashmem device ioctl should be used for a given fd.
116  * NOTE: Try not to use fstat() when possible to avoid performance issues. */
ashmem_dev_fd_check(int fd)117 static int ashmem_dev_fd_check(int fd) {
118   if (device_api_level() <= __ANDROID_API_O_MR1__)
119     return 1;
120   if (ashmem_get_status() == ASHMEM_STATUS_SUPPORTED) {
121     struct stat st;
122     return (fstat(fd, &st) == 0 && S_ISCHR(st.st_mode) &&
123             st.st_dev != 0 && st.st_dev == s_ashmem_dev);
124   }
125   return 0;
126 }
127 
128 /*
129  * ashmem_create_region - creates a new ashmem region and returns the file
130  * descriptor, or <0 on error
131  *
132  * `name' is an optional label to give the region (visible in /proc/pid/maps)
133  * `size' is the size of the region, in page-aligned bytes
134  */
ashmem_dev_create_region(const char * name,size_t size)135 static int ashmem_dev_create_region(const char *name, size_t size) {
136   int fd = open(ASHMEM_DEVICE, O_RDWR);
137   if (fd < 0)
138     return fd;
139 
140   int ret;
141   if (name) {
142     char buf[ASHMEM_NAME_LEN];
143     strlcpy(buf, name, sizeof(buf));
144     ret = ioctl(fd, ASHMEM_SET_NAME, buf);
145     if (ret < 0)
146       goto error;
147   }
148   ret = ioctl(fd, ASHMEM_SET_SIZE, size);
149   if (ret < 0)
150     goto error;
151 
152   return fd;
153 
154 error:
155   close(fd);
156   return ret;
157 }
158 
ashmem_dev_set_prot_region(int fd,int prot)159 static int ashmem_dev_set_prot_region(int fd, int prot) {
160   return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
161 }
162 
ashmem_dev_get_prot_region(int fd)163 static int ashmem_dev_get_prot_region(int fd) {
164   return ioctl(fd, ASHMEM_GET_PROT_MASK);
165 }
166 
ashmem_dev_pin_region(int fd,size_t offset,size_t len)167 static int ashmem_dev_pin_region(int fd, size_t offset, size_t len) {
168   struct ashmem_pin pin = { offset, len };
169   return ioctl(fd, ASHMEM_PIN, &pin);
170 }
171 
ashmem_dev_unpin_region(int fd,size_t offset,size_t len)172 static int ashmem_dev_unpin_region(int fd, size_t offset, size_t len) {
173   struct ashmem_pin pin = { offset, len };
174   return ioctl(fd, ASHMEM_UNPIN, &pin);
175 }
176 
ashmem_dev_get_size_region(int fd)177 static size_t ashmem_dev_get_size_region(int fd) {
178   return ioctl(fd, ASHMEM_GET_SIZE, NULL);
179 }
180 
181 // Starting with API level 26, the following functions from
182 // libandroid.so should be used to create shared memory regions.
183 typedef int(*ASharedMemory_createFunc)(const char*, size_t);
184 typedef size_t(*ASharedMemory_getSizeFunc)(int fd);
185 typedef int(*ASharedMemory_setProtFunc)(int fd, int prot);
186 
187 // Function pointers to shared memory functions.
188 typedef struct {
189   ASharedMemory_createFunc create;
190   ASharedMemory_getSizeFunc getSize;
191   ASharedMemory_setProtFunc setProt;
192 } ASharedMemoryFuncs;
193 
194 static ASharedMemoryFuncs s_ashmem_funcs = {};
195 static pthread_once_t s_ashmem_funcs_once = PTHREAD_ONCE_INIT;
196 
ashmem_init_funcs()197 static void ashmem_init_funcs() {
198   ASharedMemoryFuncs* funcs = &s_ashmem_funcs;
199   if (device_api_level() >= __ANDROID_API_O__) {
200     /* Leaked intentionally! */
201     void* lib = dlopen("libandroid.so", RTLD_NOW);
202     funcs->create =
203         (ASharedMemory_createFunc)dlsym(lib, "ASharedMemory_create");
204     funcs->getSize =
205         (ASharedMemory_getSizeFunc)dlsym(lib, "ASharedMemory_getSize");
206     funcs->setProt =
207         (ASharedMemory_setProtFunc)dlsym(lib, "ASharedMemory_setProt");
208   } else {
209     funcs->create = &ashmem_dev_create_region;
210     funcs->getSize = &ashmem_dev_get_size_region;
211     funcs->setProt = &ashmem_dev_set_prot_region;
212   }
213 }
214 
ashmem_get_funcs()215 static const ASharedMemoryFuncs* ashmem_get_funcs() {
216   pthread_once(&s_ashmem_funcs_once, ashmem_init_funcs);
217   return &s_ashmem_funcs;
218 }
219 
ashmem_create_region(const char * name,size_t size)220 int ashmem_create_region(const char* name, size_t size) {
221   return ashmem_get_funcs()->create(name, size);
222 }
223 
ashmem_set_prot_region(int fd,int prot)224 int ashmem_set_prot_region(int fd, int prot) {
225   return ashmem_get_funcs()->setProt(fd, prot);
226 }
227 
ashmem_get_prot_region(int fd)228 int ashmem_get_prot_region(int fd) {
229   if (ashmem_dev_fd_check(fd))
230     return ashmem_dev_get_prot_region(fd);
231   /* There are only two practical values to return here: either
232    * PROT_READ|PROT_WRITE or just PROT_READ, so try to determine
233    * the flags by trying to mmap() the region read-write first.
234    */
235   int result = PROT_READ;
236   const size_t page_size = (size_t)sysconf(_SC_PAGESIZE);
237   void* m = mmap(NULL, page_size, PROT_READ|PROT_WRITE,
238                  MAP_SHARED, fd, 0);
239   if (m != MAP_FAILED) {
240     munmap(m, page_size);
241     result = PROT_READ|PROT_WRITE;
242   }
243   return result;
244 }
245 
ashmem_pin_region(int fd,size_t offset,size_t len)246 int ashmem_pin_region(int fd, size_t offset, size_t len) {
247  if (ashmem_dev_fd_check(fd))
248    return ashmem_dev_pin_region(fd, offset, len);
249   return ASHMEM_NOT_PURGED;
250 }
251 
ashmem_unpin_region(int fd,size_t offset,size_t len)252 int ashmem_unpin_region(int fd, size_t offset, size_t len) {
253   if (ashmem_dev_fd_check(fd))
254     return ashmem_dev_unpin_region(fd, offset, len);
255   /* NOTE: It is not possible to use madvise() here because it requires a
256    * memory address. This could be done in the caller though, instead of
257    * this function. */
258   return 0;
259 }
260 
ashmem_get_size_region(int fd)261 int ashmem_get_size_region(int fd) {
262   /* NOTE: Original API returns an int. Avoid breaking it. */
263   return (int)ashmem_get_funcs()->getSize(fd);
264 }
265 
ashmem_device_is_supported(void)266 int ashmem_device_is_supported(void) {
267   return ashmem_get_status() == ASHMEM_STATUS_SUPPORTED;
268 }
269