• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2016, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include <openssl/cpu.h>
16 
17 #if defined(OPENSSL_ARM) && !defined(OPENSSL_STATIC_ARMCAP)
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #include <openssl/arm_arch.h>
26 #include <openssl/buf.h>
27 #include <openssl/mem.h>
28 
29 #include "internal.h"
30 
31 
32 #define AT_HWCAP 16
33 #define AT_HWCAP2 26
34 
35 #define HWCAP_NEON (1 << 12)
36 
37 /* See /usr/include/asm/hwcap.h on an ARM installation for the source of
38  * these values. */
39 #define HWCAP2_AES (1 << 0)
40 #define HWCAP2_PMULL (1 << 1)
41 #define HWCAP2_SHA1 (1 << 2)
42 #define HWCAP2_SHA2 (1 << 3)
43 
44 /* |getauxval| is not available on Android until API level 20. Link it as a weak
45  * symbol and use other methods as fallback. */
46 unsigned long getauxval(unsigned long type) __attribute__((weak));
47 
open_eintr(const char * path,int flags)48 static int open_eintr(const char *path, int flags) {
49   int ret;
50   do {
51     ret = open(path, flags);
52   } while (ret < 0 && errno == EINTR);
53   return ret;
54 }
55 
read_eintr(int fd,void * out,size_t len)56 static ssize_t read_eintr(int fd, void *out, size_t len) {
57   ssize_t ret;
58   do {
59     ret = read(fd, out, len);
60   } while (ret < 0 && errno == EINTR);
61   return ret;
62 }
63 
64 /* read_full reads exactly |len| bytes from |fd| to |out|. On error or end of
65  * file, it returns zero. */
read_full(int fd,void * out,size_t len)66 static int read_full(int fd, void *out, size_t len) {
67   char *outp = out;
68   while (len > 0) {
69     ssize_t ret = read_eintr(fd, outp, len);
70     if (ret <= 0) {
71       return 0;
72     }
73     outp += ret;
74     len -= ret;
75   }
76   return 1;
77 }
78 
79 /* read_file opens |path| and reads until end-of-file. On success, it returns
80  * one and sets |*out_ptr| and |*out_len| to a newly-allocated buffer with the
81  * contents. Otherwise, it returns zero. */
read_file(char ** out_ptr,size_t * out_len,const char * path)82 static int read_file(char **out_ptr, size_t *out_len, const char *path) {
83   int fd = open_eintr(path, O_RDONLY);
84   if (fd < 0) {
85     return 0;
86   }
87 
88   static const size_t kReadSize = 1024;
89   int ret = 0;
90   size_t cap = kReadSize, len = 0;
91   char *buf = OPENSSL_malloc(cap);
92   if (buf == NULL) {
93     goto err;
94   }
95 
96   for (;;) {
97     if (cap - len < kReadSize) {
98       size_t new_cap = cap * 2;
99       if (new_cap < cap) {
100         goto err;
101       }
102       char *new_buf = OPENSSL_realloc(buf, new_cap);
103       if (new_buf == NULL) {
104         goto err;
105       }
106       buf = new_buf;
107       cap = new_cap;
108     }
109 
110     ssize_t bytes_read = read_eintr(fd, buf + len, kReadSize);
111     if (bytes_read < 0) {
112       goto err;
113     }
114     if (bytes_read == 0) {
115       break;
116     }
117     len += bytes_read;
118   }
119 
120   *out_ptr = buf;
121   *out_len = len;
122   ret = 1;
123   buf = NULL;
124 
125 err:
126   OPENSSL_free(buf);
127   close(fd);
128   return ret;
129 }
130 
131 /* getauxval_proc behaves like |getauxval| but reads from /proc/self/auxv. */
getauxval_proc(unsigned long type)132 static unsigned long getauxval_proc(unsigned long type) {
133   int fd = open_eintr("/proc/self/auxv", O_RDONLY);
134   if (fd < 0) {
135     return 0;
136   }
137 
138   struct {
139     unsigned long tag;
140     unsigned long value;
141   } entry;
142 
143   for (;;) {
144     if (!read_full(fd, &entry, sizeof(entry)) ||
145         (entry.tag == 0 && entry.value == 0)) {
146       break;
147     }
148     if (entry.tag == type) {
149       close(fd);
150       return entry.value;
151     }
152   }
153   close(fd);
154   return 0;
155 }
156 
157 typedef struct {
158   const char *data;
159   size_t len;
160 } STRING_PIECE;
161 
STRING_PIECE_equals(const STRING_PIECE * a,const char * b)162 static int STRING_PIECE_equals(const STRING_PIECE *a, const char *b) {
163   size_t b_len = strlen(b);
164   return a->len == b_len && OPENSSL_memcmp(a->data, b, b_len) == 0;
165 }
166 
167 /* STRING_PIECE_split finds the first occurence of |sep| in |in| and, if found,
168  * sets |*out_left| and |*out_right| to |in| split before and after it. It
169  * returns one if |sep| was found and zero otherwise. */
STRING_PIECE_split(STRING_PIECE * out_left,STRING_PIECE * out_right,const STRING_PIECE * in,char sep)170 static int STRING_PIECE_split(STRING_PIECE *out_left, STRING_PIECE *out_right,
171                               const STRING_PIECE *in, char sep) {
172   const char *p = OPENSSL_memchr(in->data, sep, in->len);
173   if (p == NULL) {
174     return 0;
175   }
176   /* |out_left| or |out_right| may alias |in|, so make a copy. */
177   STRING_PIECE in_copy = *in;
178   out_left->data = in_copy.data;
179   out_left->len = p - in_copy.data;
180   out_right->data = in_copy.data + out_left->len + 1;
181   out_right->len = in_copy.len - out_left->len - 1;
182   return 1;
183 }
184 
185 /* STRING_PIECE_trim removes leading and trailing whitespace from |s|. */
STRING_PIECE_trim(STRING_PIECE * s)186 static void STRING_PIECE_trim(STRING_PIECE *s) {
187   while (s->len != 0 && (s->data[0] == ' ' || s->data[0] == '\t')) {
188     s->data++;
189     s->len--;
190   }
191   while (s->len != 0 &&
192          (s->data[s->len - 1] == ' ' || s->data[s->len - 1] == '\t')) {
193     s->len--;
194   }
195 }
196 
197 /* extract_cpuinfo_field extracts a /proc/cpuinfo field named |field| from
198  * |in|.  If found, it sets |*out| to the value and returns one. Otherwise, it
199  * returns zero. */
extract_cpuinfo_field(STRING_PIECE * out,const STRING_PIECE * in,const char * field)200 static int extract_cpuinfo_field(STRING_PIECE *out, const STRING_PIECE *in,
201                                  const char *field) {
202   /* Process |in| one line at a time. */
203   STRING_PIECE remaining = *in, line;
204   while (STRING_PIECE_split(&line, &remaining, &remaining, '\n')) {
205     STRING_PIECE key, value;
206     if (!STRING_PIECE_split(&key, &value, &line, ':')) {
207       continue;
208     }
209     STRING_PIECE_trim(&key);
210     if (STRING_PIECE_equals(&key, field)) {
211       STRING_PIECE_trim(&value);
212       *out = value;
213       return 1;
214     }
215   }
216 
217   return 0;
218 }
219 
cpuinfo_field_equals(const STRING_PIECE * cpuinfo,const char * field,const char * value)220 static int cpuinfo_field_equals(const STRING_PIECE *cpuinfo, const char *field,
221                                 const char *value) {
222   STRING_PIECE extracted;
223   return extract_cpuinfo_field(&extracted, cpuinfo, field) &&
224          STRING_PIECE_equals(&extracted, value);
225 }
226 
227 /* has_list_item treats |list| as a space-separated list of items and returns
228  * one if |item| is contained in |list| and zero otherwise. */
has_list_item(const STRING_PIECE * list,const char * item)229 static int has_list_item(const STRING_PIECE *list, const char *item) {
230   STRING_PIECE remaining = *list, feature;
231   while (STRING_PIECE_split(&feature, &remaining, &remaining, ' ')) {
232     if (STRING_PIECE_equals(&feature, item)) {
233       return 1;
234     }
235   }
236   return 0;
237 }
238 
get_hwcap_cpuinfo(const STRING_PIECE * cpuinfo)239 static unsigned long get_hwcap_cpuinfo(const STRING_PIECE *cpuinfo) {
240   if (cpuinfo_field_equals(cpuinfo, "CPU architecture", "8")) {
241     /* This is a 32-bit ARM binary running on a 64-bit kernel. NEON is always
242      * available on ARMv8. Linux omits required features, so reading the
243      * "Features" line does not work. (For simplicity, use strict equality. We
244      * assume everything running on future ARM architectures will have a
245      * working |getauxval|.) */
246     return HWCAP_NEON;
247   }
248 
249   STRING_PIECE features;
250   if (extract_cpuinfo_field(&features, cpuinfo, "Features") &&
251       has_list_item(&features, "neon")) {
252     return HWCAP_NEON;
253   }
254   return 0;
255 }
256 
get_hwcap2_cpuinfo(const STRING_PIECE * cpuinfo)257 static unsigned long get_hwcap2_cpuinfo(const STRING_PIECE *cpuinfo) {
258   STRING_PIECE features;
259   if (!extract_cpuinfo_field(&features, cpuinfo, "Features")) {
260     return 0;
261   }
262 
263   unsigned long ret = 0;
264   if (has_list_item(&features, "aes")) {
265     ret |= HWCAP2_AES;
266   }
267   if (has_list_item(&features, "pmull")) {
268     ret |= HWCAP2_PMULL;
269   }
270   if (has_list_item(&features, "sha1")) {
271     ret |= HWCAP2_SHA1;
272   }
273   if (has_list_item(&features, "sha2")) {
274     ret |= HWCAP2_SHA2;
275   }
276   return ret;
277 }
278 
279 /* has_broken_neon returns one if |in| matches a CPU known to have a broken
280  * NEON unit. See https://crbug.com/341598. */
has_broken_neon(const STRING_PIECE * cpuinfo)281 static int has_broken_neon(const STRING_PIECE *cpuinfo) {
282   return cpuinfo_field_equals(cpuinfo, "CPU implementer", "0x51") &&
283          cpuinfo_field_equals(cpuinfo, "CPU architecture", "7") &&
284          cpuinfo_field_equals(cpuinfo, "CPU variant", "0x1") &&
285          cpuinfo_field_equals(cpuinfo, "CPU part", "0x04d") &&
286          cpuinfo_field_equals(cpuinfo, "CPU revision", "0");
287 }
288 
289 extern uint32_t OPENSSL_armcap_P;
290 
291 static int g_has_broken_neon;
292 
OPENSSL_cpuid_setup(void)293 void OPENSSL_cpuid_setup(void) {
294   char *cpuinfo_data;
295   size_t cpuinfo_len;
296   if (!read_file(&cpuinfo_data, &cpuinfo_len, "/proc/cpuinfo")) {
297     return;
298   }
299   STRING_PIECE cpuinfo;
300   cpuinfo.data = cpuinfo_data;
301   cpuinfo.len = cpuinfo_len;
302 
303   /* |getauxval| is not available on Android until API level 20. If it is
304    * unavailable, read from /proc/self/auxv as a fallback. This is unreadable
305    * on some versions of Android, so further fall back to /proc/cpuinfo.
306    *
307    * See
308    * https://android.googlesource.com/platform/ndk/+/882ac8f3392858991a0e1af33b4b7387ec856bd2
309    * and b/13679666 (Google-internal) for details. */
310   unsigned long hwcap = 0;
311   if (getauxval != NULL) {
312     hwcap = getauxval(AT_HWCAP);
313   }
314   if (hwcap == 0) {
315     hwcap = getauxval_proc(AT_HWCAP);
316   }
317   if (hwcap == 0) {
318     hwcap = get_hwcap_cpuinfo(&cpuinfo);
319   }
320 
321   /* Clear NEON support if known broken. */
322   g_has_broken_neon = has_broken_neon(&cpuinfo);
323   if (g_has_broken_neon) {
324     hwcap &= ~HWCAP_NEON;
325   }
326 
327   /* Matching OpenSSL, only report other features if NEON is present. */
328   if (hwcap & HWCAP_NEON) {
329     OPENSSL_armcap_P |= ARMV7_NEON;
330 
331     /* Some ARMv8 Android devices don't expose AT_HWCAP2. Fall back to
332      * /proc/cpuinfo. See https://crbug.com/596156. */
333     unsigned long hwcap2 = 0;
334     if (getauxval != NULL) {
335       hwcap2 = getauxval(AT_HWCAP2);
336     }
337     if (hwcap2 == 0) {
338       hwcap2 = get_hwcap2_cpuinfo(&cpuinfo);
339     }
340 
341     if (hwcap2 & HWCAP2_AES) {
342       OPENSSL_armcap_P |= ARMV8_AES;
343     }
344     if (hwcap2 & HWCAP2_PMULL) {
345       OPENSSL_armcap_P |= ARMV8_PMULL;
346     }
347     if (hwcap2 & HWCAP2_SHA1) {
348       OPENSSL_armcap_P |= ARMV8_SHA1;
349     }
350     if (hwcap2 & HWCAP2_SHA2) {
351       OPENSSL_armcap_P |= ARMV8_SHA256;
352     }
353   }
354 
355   OPENSSL_free(cpuinfo_data);
356 }
357 
CRYPTO_has_broken_NEON(void)358 int CRYPTO_has_broken_NEON(void) { return g_has_broken_neon; }
359 
360 #endif /* OPENSSL_ARM && !OPENSSL_STATIC_ARMCAP */
361